├── .DS_Store ├── .flowconfig ├── .gitignore ├── LICENSE ├── README.md ├── build.sh ├── docs ├── .vuepress │ ├── config.js │ └── public │ │ ├── assets │ │ ├── advance-1.png │ │ ├── advance-2.png │ │ ├── dom.png │ │ ├── event-loop.png │ │ ├── lifecycle.png │ │ ├── mind.png │ │ ├── new-vue.png │ │ ├── parse.png │ │ ├── qq.jpg │ │ ├── qrcode.png │ │ ├── qrcode_mp.jpg │ │ ├── reactive.png │ │ ├── stack.png │ │ ├── update-children-1.png │ │ ├── update-children-2.png │ │ ├── update-children-3.png │ │ ├── update-children-4.png │ │ ├── update-children-5.png │ │ ├── update-children-6.png │ │ ├── vuex.png │ │ └── vuex1.png │ │ ├── icons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── msapplication-icon-144x144.png │ │ ├── mstile-150x150.png │ │ └── safari-pinned-tab.svg │ │ ├── logo.png │ │ └── manifest.json ├── README.md ├── v2 │ ├── compile │ │ ├── codegen.md │ │ ├── entrance.md │ │ ├── index.md │ │ ├── optimize.md │ │ └── parse.md │ ├── components │ │ ├── async-component.md │ │ ├── component-register.md │ │ ├── create-component.md │ │ ├── index.md │ │ ├── lifecycle.md │ │ ├── merge-option.md │ │ └── patch.md │ ├── data-driven │ │ ├── create-element.md │ │ ├── index.md │ │ ├── mounted.md │ │ ├── new-vue.md │ │ ├── render.md │ │ ├── update.md │ │ └── virtual-dom.md │ ├── extend │ │ ├── event.md │ │ ├── index.md │ │ ├── keep-alive.md │ │ ├── slot.md │ │ ├── tansition-group.md │ │ ├── tansition.md │ │ └── v-model.md │ ├── prepare │ │ ├── build.md │ │ ├── directory.md │ │ ├── entrance.md │ │ ├── flow.md │ │ └── index.md │ ├── reactive │ │ ├── component-update.md │ │ ├── computed-watcher.md │ │ ├── getters.md │ │ ├── index.md │ │ ├── next-tick.md │ │ ├── props.md │ │ ├── questions.md │ │ ├── reactive-object.md │ │ ├── setters.md │ │ └── summary.md │ ├── vue-router │ │ ├── index.md │ │ ├── install.md │ │ ├── matcher.md │ │ ├── router.md │ │ └── transition-to.md │ └── vuex │ │ ├── api.md │ │ ├── index.md │ │ ├── init.md │ │ └── plugin.md └── v3 │ ├── guide │ └── index.md │ └── new │ └── index.md ├── package-lock.json ├── package.json ├── vue-router ├── build │ ├── build.js │ ├── configs.js │ ├── release.sh │ ├── rollup.dev.config.js │ └── update-docs.sh ├── dist │ ├── vue-router.common.js │ ├── vue-router.esm.js │ ├── vue-router.js │ └── vue-router.min.js ├── flow │ └── declarations.js ├── src │ ├── components │ │ ├── link.js │ │ └── view.js │ ├── create-matcher.js │ ├── create-route-map.js │ ├── history │ │ ├── abstract.js │ │ ├── base.js │ │ ├── hash.js │ │ └── html5.js │ ├── index.js │ ├── install.js │ └── util │ │ ├── async.js │ │ ├── dom.js │ │ ├── location.js │ │ ├── params.js │ │ ├── path.js │ │ ├── push-state.js │ │ ├── query.js │ │ ├── resolve-components.js │ │ ├── route.js │ │ ├── scroll.js │ │ └── warn.js └── test │ ├── e2e │ ├── nightwatch.config.js │ ├── runner.js │ └── specs │ │ ├── active-links.js │ │ ├── auth-flow.js │ │ ├── basic.js │ │ ├── data-fetching.js │ │ ├── hash-mode.js │ │ ├── hash-scroll-behavior.js │ │ ├── lazy-loading.js │ │ ├── named-routes.js │ │ ├── named-views.js │ │ ├── navigation-guards.js │ │ ├── nested-router.js │ │ ├── nested-routes.js │ │ ├── redirect.js │ │ ├── route-alias.js │ │ ├── route-matching.js │ │ ├── route-props.js │ │ ├── scroll-behavior.js │ │ └── transitions.js │ └── unit │ ├── jasmine.json │ └── specs │ ├── api.spec.js │ ├── async.spec.js │ ├── create-map.spec.js │ ├── create-matcher.spec.js │ ├── custom-query.spec.js │ ├── discrete-components.spec.js │ ├── error-handling.spec.js │ ├── location.spec.js │ ├── node.spec.js │ ├── path.spec.js │ ├── query.spec.js │ └── route.spec.js ├── vue ├── dist │ ├── README.md │ ├── vue.common.js │ ├── vue.esm.browser.js │ ├── vue.esm.js │ ├── vue.js │ ├── vue.min.js │ ├── vue.runtime.common.js │ ├── vue.runtime.esm.js │ ├── vue.runtime.js │ └── vue.runtime.min.js ├── flow │ ├── compiler.js │ ├── component.js │ ├── global-api.js │ ├── modules.js │ ├── options.js │ ├── ssr.js │ ├── vnode.js │ └── weex.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 │ │ ├── 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 ├── vuex ├── build │ ├── build.main.js │ ├── configs.js │ ├── release.sh │ ├── rollup.dev.config.js │ └── rollup.logger.config.js ├── dist │ ├── logger.d.ts │ ├── logger.js │ ├── vuex.common.js │ ├── vuex.esm.js │ ├── vuex.js │ └── vuex.min.js ├── src │ ├── helpers.js │ ├── index.esm.js │ ├── index.js │ ├── mixin.js │ ├── module │ │ ├── module-collection.js │ │ └── module.js │ ├── plugins │ │ ├── devtool.js │ │ └── logger.js │ ├── store.js │ └── util.js └── test │ ├── e2e │ ├── nightwatch.config.js │ ├── runner.js │ └── specs │ │ ├── cart.js │ │ ├── chat.js │ │ ├── counter.js │ │ └── todomvc.js │ └── unit │ ├── .eslintrc │ ├── helpers.spec.js │ ├── hot-reload.spec.js │ ├── jasmine.json │ ├── module │ ├── module-collection.spec.js │ └── module.spec.js │ ├── modules.spec.js │ ├── setup.js │ ├── store.spec.js │ └── util.spec.js └── yarn.lock /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/.DS_Store -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/.* 3 | .*/test/.* 4 | .*/build/.* 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 | .idea/ 2 | node_modules 3 | dist 4 | example/ 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 HuangYi 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue.js 技术揭秘 2 | 3 | [电子书](https://ustbhuangyi.github.io/vue-analysis/) 4 | 5 | 目前社区有很多 Vue.js 的源码解析文章,但是质量层次不齐,不够系统和全面,这本电子书的目标是全方位细致深度解析 Vue.js 的实现原理,让同学们可以彻底掌握 Vue.js。目前分析的版本是 Vue.js 的最新版本 Vue.js 2.5.17-beta.0,并且之后会随着版本升级而做相应的更新,充分发挥电子书的优势。 6 | 7 | 这本电子书是作为 [《Vue.js 源码揭秘》](http://coding.imooc.com/class/228.html)视频课程的辅助教材。电子书是开源的,同学们可以免费阅读,视频是收费的,25+小时纯干货课程,如果有需要的同学可以购买来学习,**但请务必支持正版,请尊重作者的劳动成果**。 8 | 9 | ## 章节目录 10 | 11 | 为了把 Vue.js 的源码讲明白,课程设计成由浅入深,分为核心、编译、扩展、生态四个方面去讲,并拆成了八个章节,如下图: 12 | 13 | 14 | 15 | **第一章:准备工作** 16 | 17 | 介绍了 Flow、Vue.js 的源码目录设计、Vue.js 的源码构建方式,以及从入口开始分析了 Vue.js 的初始化过程。 18 | 19 | **第二章:数据驱动** 20 | 21 | 详细讲解了模板数据到 DOM 渲染的过程,从 `new Vue` 开始,分析了 `mount`、`render`、`update`、`patch` 等流程。 22 | 23 | **第三章:组件化** 24 | 25 | 分析了组件化的实现原理,并且分析了组件周边的原理实现,包括合并配置、生命周期、组件注册、异步组件。 26 | 27 | **第四章:深入响应式原理** 28 | 29 | 详细讲解了数据的变化如何驱动视图的变化,分析了响应式对象的创建,依赖收集、派发更新的实现过程,一些特殊情况的处理,并对比了计算属性和侦听属性的实现,最后分析了组件更新的过程。 30 | 31 | **第五章:编译** 32 | 33 | 从编译的入口函数开始,分析了编译的三个核心流程的实现:`parse` -> `optimize` -> `codegen`。 34 | 35 | **第六章:扩展** 36 | 37 | 详细讲解了 `event`、`v-model`、`slot`、`keep-alive`、`transition`、`transition-group` 等常用功能的原理实现,该章节作为一个可扩展章节,未来会分析更多 Vue 提供的特性。 38 | 39 | **第七章:Vue-Router** 40 | 41 | 分析了 Vue-Router 的实现原理,从路由注册开始,分析了路由对象、`matcher`,并深入分析了整个路径切换的实现过程和细节。 42 | 43 | **第八章:Vuex** 44 | 45 | 分析了 Vuex 的实现原理,深入分析了它的初始化过程,常用 API 以及插件部分的实现。 46 | 47 | 48 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | npm run build 5 | 6 | cd dist 7 | 8 | git init 9 | git add -A 10 | git commit -m 'deploy' 11 | 12 | git push -f git@github.com:ustbhuangyi/vue-analysis.git master:gh-pages 13 | 14 | cd - 15 | -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/advance-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/advance-1.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/advance-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/advance-2.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/dom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/dom.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/event-loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/event-loop.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/lifecycle.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/mind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/mind.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/new-vue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/new-vue.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/parse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/parse.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/qq.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/qq.jpg -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/qrcode.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/qrcode_mp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/qrcode_mp.jpg -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/reactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/reactive.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/stack.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/update-children-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/update-children-1.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/update-children-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/update-children-2.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/update-children-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/update-children-3.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/update-children-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/update-children-4.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/update-children-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/update-children-5.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/update-children-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/update-children-6.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/vuex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/vuex.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/vuex1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/assets/vuex1.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/icons/favicon-16x16.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/icons/favicon-32x32.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/icons/mstile-150x150.png -------------------------------------------------------------------------------- /docs/.vuepress/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/docs/.vuepress/public/logo.png -------------------------------------------------------------------------------- /docs/.vuepress/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "VueAnalysis", 3 | "short_name": "VueAnalysis", 4 | "icons": [ 5 | { 6 | "src": "/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "/vue-analysis/", 17 | "display": "standalone", 18 | "background_color": "#fff", 19 | "theme_color": "#3eaf7c" 20 | } 21 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 目前社区有很多 Vue.js 的源码解析文章,但是质量层次不齐,不够系统和全面,这本电子书的目标是全方位细致深度解析 Vue.js 的实现原理,让同学们可以彻底掌握 Vue.js。目前分析的版本是 Vue.js 的最新版本 Vue.js 2.5.17-beta.0,并且之后会随着版本升级而做相应的更新,充分发挥电子书的优势。 4 | 5 | 这本电子书是作为 [《Vue.js 源码揭秘》](http://coding.imooc.com/class/228.html)视频课程的辅助教材。电子书是开源的,同学们可以免费阅读,视频是收费的,25+小时纯干货课程,如果有需要的同学可以购买来学习,**但请务必支持正版,请尊重作者的劳动成果**。 6 | 7 | ## 章节目录 8 | 9 | 为了把 Vue.js 的源码讲明白,课程设计成由浅入深,分为核心、编译、扩展、生态四个方面去讲,并拆成了八个章节,如下图: 10 | 11 | 12 | 13 | **第一章:准备工作** 14 | 15 | 介绍了 Flow、Vue.js 的源码目录设计、Vue.js 的源码构建方式,以及从入口开始分析了 Vue.js 的初始化过程。 16 | 17 | **第二章:数据驱动** 18 | 19 | 详细讲解了模板数据到 DOM 渲染的过程,从 `new Vue` 开始,分析了 `mount`、`render`、`update`、`patch` 等流程。 20 | 21 | **第三章:组件化** 22 | 23 | 分析了组件化的实现原理,并且分析了组件周边的原理实现,包括合并配置、生命周期、组件注册、异步组件。 24 | 25 | **第四章:深入响应式原理** 26 | 27 | 详细讲解了数据的变化如何驱动视图的变化,分析了响应式对象的创建,依赖收集、派发更新的实现过程,一些特殊情况的处理,并对比了计算属性和侦听属性的实现,最后分析了组件更新的过程。 28 | 29 | **第五章:编译** 30 | 31 | 从编译的入口函数开始,分析了编译的三个核心流程的实现:`parse` -> `optimize` -> `codegen`。 32 | 33 | **第六章:扩展** 34 | 35 | 详细讲解了 `event`、`v-model`、`slot`、`keep-alive`、`transition`、`transition-group` 等常用功能的原理实现,该章节作为一个可扩展章节,未来会分析更多 Vue 提供的特性。 36 | 37 | **第七章:Vue-Router** 38 | 39 | 分析了 Vue-Router 的实现原理,从路由注册开始,分析了路由对象、`matcher`,并深入分析了整个路径切换的实现过程和细节。 40 | 41 | **第八章:Vuex** 42 | 43 | 分析了 Vuex 的实现原理,深入分析了它的初始化过程,常用 API 以及插件部分的实现。 44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/v2/compile/index.md: -------------------------------------------------------------------------------- 1 | # 编译 2 | 3 | 之前我们分析过模板到真实 DOM 渲染的过程,中间有一个环节是把模板编译成 `render` 函数,这个过程我们把它称作编译。 4 | 5 | 虽然我们可以直接为组件编写 `render` 函数,但是编写 `template` 模板更加直观,也更符合我们的开发习惯。 6 | 7 | Vue.js 提供了 2 个版本,一个是 Runtime + Compiler 的,一个是 Runtime only 的,前者是包含编译代码的,可以把编译过程放在运行时做,后者是不包含编译代码的,需要借助 webpack 的 `vue-loader` 事先把模板编译成 `render `函数。 8 | 9 | 这一章我们就来分析编译的过程,对编译过程的了解会让我们对 Vue 的指令、内置组件等有更好的理解。不过由于编译的过程是一个相对复杂的过程,我们只要求理解整体的流程、输入和输出即可,对于细节我们不必抠太细。有些细节比如对于 `slot` 的处理我们可以在之后去分析插槽实现的时候再详细分析。 -------------------------------------------------------------------------------- /docs/v2/components/index.md: -------------------------------------------------------------------------------- 1 | # 组件化 2 | 3 | Vue.js 另一个核心思想是组件化。所谓组件化,就是把页面拆分成多个组件 (component),每个组件依赖的 CSS、JavaScript、模板、图片等资源放在一起开发和维护。组件是资源独立的,组件在系统内部可复用,组件和组件之间可以嵌套。 4 | 5 | 我们在用 Vue.js 开发实际项目的时候,就是像搭积木一样,编写一堆组件拼装生成页面。在 Vue.js 的官网中,也是花了大篇幅来介绍什么是组件,如何编写组件以及组件拥有的属性和特性。 6 | 7 | 那么在这一章节,我们将从源码的角度来分析 Vue 的组件内部是如何工作的,只有了解了内部的工作原理,才能让我们使用它的时候更加得心应手。 8 | 9 | 接下来我们会用 Vue-cli 初始化的代码为例,来分析一下 Vue 组件初始化的一个过程。 10 | 11 | ```js 12 | import Vue from 'vue' 13 | import App from './App.vue' 14 | 15 | var app = new Vue({ 16 | el: '#app', 17 | // 这里的 h 是 createElement 方法 18 | render: h => h(App) 19 | }) 20 | ``` 21 | 这段代码相信很多同学都很熟悉,它和我们上一章相同的点也是通过 `render` 函数去渲染的,不同的这次通过 `createElement` 传的参数是一个组件而不是一个原生的标签,那么接下来我们就开始分析这一过程。 -------------------------------------------------------------------------------- /docs/v2/data-driven/index.md: -------------------------------------------------------------------------------- 1 | # 数据驱动 2 | 3 | Vue.js 一个核心思想是数据驱动。所谓数据驱动,是指视图是由数据驱动生成的,我们对视图的修改,不会直接操作 DOM,而是通过修改数据。它相比我们传统的前端开发,如使用 jQuery 等前端库直接修改 DOM,大大简化了代码量。特别是当交互复杂的时候,只关心数据的修改会让代码的逻辑变的非常清晰,因为 DOM 变成了数据的映射,我们所有的逻辑都是对数据的修改,而不用碰触 DOM,这样的代码非常利于维护。 4 | 5 | 在 Vue.js 中我们可以采用简洁的模板语法来声明式的将数据渲染为 DOM: 6 | 7 | ```html 8 |
9 | {{ message }} 10 |
11 | ``` 12 | 13 | ```js 14 | var app = new Vue({ 15 | el: '#app', 16 | data: { 17 | message: 'Hello Vue!' 18 | } 19 | }) 20 | ``` 21 | 22 | 最终它会在页面上渲染出 `Hello Vue`。接下来,我们会从源码角度来分析 Vue 是如何实现的,分析过程会以主线代码为主,重要的分支逻辑会放在之后单独分析。数据驱动还有一部分是数据更新驱动视图变化,这一块内容我们也会在之后的章节分析,这一章我们的目标是弄清楚模板和数据如何渲染成最终的 DOM。 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /docs/v2/extend/index.md: -------------------------------------------------------------------------------- 1 | # 扩展 2 | 3 | 前面几章我们分析了 Vue 的核心以及编译过程,除此之外,Vue 还提供了很多好用的 feature 如 `event`、`v-model`、`slot`、`keep-alive`、`transition` 等等。对他们的理解有助于我们在平时开发中更好地应用这些 feature,即使出现 bug 我们也可以很从容地应对。 4 | 5 | 这一章是一个可扩展的章节,除了已分析的这些 feature 外,未来我们可能会扩展更多的内容。 -------------------------------------------------------------------------------- /docs/v2/prepare/directory.md: -------------------------------------------------------------------------------- 1 | # Vue.js 源码目录设计 2 | 3 | Vue.js 的源码都在 src 目录下,其目录结构如下。 4 | 5 | ``` 6 | src 7 | ├── compiler # 编译相关 8 | ├── core # 核心代码 9 | ├── platforms # 不同平台的支持 10 | ├── server # 服务端渲染 11 | ├── sfc # .vue 文件解析 12 | ├── shared # 共享代码 13 | ``` 14 | 15 | ## compiler 16 | 17 | compiler 目录包含 Vue.js 所有编译相关的代码。它包括把模板解析成 ast 语法树,ast 语法树优化,代码生成等功能。 18 | 19 | 编译的工作可以在构建时做(借助 webpack、vue-loader 等辅助插件);也可以在运行时做,使用包含构建功能的 Vue.js。显然,编译是一项耗性能的工作,所以更推荐前者——离线编译。 20 | 21 | ## core 22 | 23 | core 目录包含了 Vue.js 的核心代码,包括内置组件、全局 API 封装,Vue 实例化、观察者、虚拟 DOM、工具函数等等。 24 | 25 | 这里的代码可谓是 Vue.js 的灵魂,也是我们之后需要重点分析的地方。 26 | 27 | ## platform 28 | 29 | Vue.js 是一个跨平台的 MVVM 框架,它可以跑在 web 上,也可以配合 weex 跑在 native 客户端上。platform 是 Vue.js 的入口,2 个目录代表 2 个主要入口,分别打包成运行在 web 上和 weex 上的 Vue.js。 30 | 31 | 我们会重点分析 web 入口打包后的 Vue.js,对于 weex 入口打包的 Vue.js,感兴趣的同学可以自行研究。 32 | 33 | ## server 34 | 35 | Vue.js 2.0 支持了服务端渲染,所有服务端渲染相关的逻辑都在这个目录下。注意:这部分代码是跑在服务端的 Node.js,不要和跑在浏览器端的 Vue.js 混为一谈。 36 | 37 | 服务端渲染主要的工作是把组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序。 38 | 39 | ## sfc 40 | 41 | 通常我们开发 Vue.js 都会借助 webpack 构建, 然后通过 .vue 单文件来编写组件。 42 | 43 | 这个目录下的代码逻辑会把 .vue 文件内容解析成一个 JavaScript 的对象。 44 | 45 | ## shared 46 | 47 | Vue.js 会定义一些工具方法,这里定义的工具方法都是会被浏览器端的 Vue.js 和服务端的 Vue.js 所共享的。 48 | 49 | ## 总结 50 | 51 | 从 Vue.js 的目录设计可以看到,作者把功能模块拆分的非常清楚,相关的逻辑放在一个独立的目录下维护,并且把复用的代码也抽成一个独立目录。 52 | 53 | 这样的目录设计让代码的阅读性和可维护性都变强,是非常值得学习和推敲的。 54 | 55 | -------------------------------------------------------------------------------- /docs/v2/prepare/index.md: -------------------------------------------------------------------------------- 1 | # 准备工作 2 | 3 | 那么从这一章开始我们即将分析 Vue 的源码,我们将会介绍一些前置知识如 flow、源码目录、构建方式、编译入口等。 4 | 5 | 除此之外,我希望你已经用过 Vue 做过 2 个以上的实际项目,对 Vue 的思想有了一定的了解,对绝大部分的 API 都已经有使用。同时,我也要求你有一定的原生 JavaScript 的功底,并对代码调试有一定的了解。 6 | 7 | 如果你具备了以上条件,并且对 Vue 的实现原理很感兴趣,那么就可以开始这门课程的学习了,我将会为你打开 Vue 的底层世界大门,对它的实现细节一探究竟。 -------------------------------------------------------------------------------- /docs/v2/reactive/index.md: -------------------------------------------------------------------------------- 1 | # 深入响应式原理 2 | 3 | 前面 2 章介绍的都是 Vue 怎么实现数据渲染和组件化的,主要讲的是初始化的过程,把原始的数据最终映射到 DOM 中,但并没有涉及到数据变化到 DOM 变化的部分。而 Vue 的数据驱动除了数据渲染 DOM 之外,还有一个很重要的体现就是数据的变更会触发 DOM 的变化。 4 | 5 | 其实前端开发最重要的 2 个工作,一个是把数据渲染到页面,另一个是处理用户交互。Vue 把数据渲染到页面的能力我们已经通过源码分析出其中的原理了,但是由于一些用户交互或者是其它方面导致数据发生变化重新对页面渲染的原理我们还未分析。 6 | 7 | 考虑如下示例: 8 | 9 | ```html 10 |
11 | {{ message }} 12 |
13 | ``` 14 | 15 | ```js 16 | var app = new Vue({ 17 | el: '#app', 18 | data: { 19 | message: 'Hello Vue!' 20 | }, 21 | methods: { 22 | changeMsg() { 23 | this.message = 'Hello World!' 24 | } 25 | } 26 | }) 27 | ``` 28 | 当我们去修改 `this.message` 的时候,模板对应的插值也会渲染成新的数据,那么这一切是怎么做到的呢? 29 | 30 | 在分析前,我们先直观的想一下,如果不用 Vue 的话,我们会通过最简单的方法实现这个需求:监听点击事件,修改数据,手动操作 DOM 重新渲染。这个过程和使用 Vue 的最大区别就是多了一步“手动操作 DOM 重新渲染”。这一步看上去并不多,但它背后又潜在的几个要处理的问题: 31 | 32 | 1. 我需要修改哪块的 DOM? 33 | 34 | 2. 我的修改效率和性能是不是最优的? 35 | 36 | 3. 我需要对数据每一次的修改都去操作 DOM 吗? 37 | 38 | 4. 我需要 case by case 去写修改 DOM 的逻辑吗? 39 | 40 | 如果我们使用了 Vue,那么上面几个问题 Vue 内部就帮你做了,那么 Vue 是如何在我们对数据修改后自动做这些事情呢,接下来我们将进入一些 Vue 响应式系统的底层的细节。 -------------------------------------------------------------------------------- /docs/v2/reactive/summary.md: -------------------------------------------------------------------------------- 1 | # 原理图 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/v2/vuex/index.md: -------------------------------------------------------------------------------- 1 | # Vuex 2 | 3 | Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 4 | 5 | ## 什么是“状态管理模式”? 6 | 7 | 让我们从一个简单的 Vue 计数应用开始: 8 | 9 | ```js 10 | new Vue({ 11 | // state 12 | data () { 13 | return { 14 | count: 0 15 | } 16 | }, 17 | // view 18 | template: ` 19 |
{{ count }}
20 | `, 21 | // actions 22 | methods: { 23 | increment () { 24 | this.count++ 25 | } 26 | } 27 | }) 28 | ``` 29 | 30 | 这个状态自管理应用包含以下几个部分: 31 | 32 | - state,驱动应用的数据源; 33 | - view,以声明方式将 state 映射到视图; 34 | - actions,响应在 view 上的用户输入导致的状态变化。 35 | 36 | 以下是一个表示“单向数据流”理念的极简示意: 37 | 38 | 39 | 40 | 但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏: 41 | 42 | - 多个视图依赖于同一状态。 43 | - 来自不同视图的行为需要变更同一状态。 44 | 45 | 对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。 46 | 47 | 因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为。 48 | 49 | ## Vuex 核心思想 50 | 51 | Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。有些同学可能会问,那我定义一个全局对象,再去上层封装了一些数据存取的接口不也可以么? 52 | 53 | Vuex 和单纯的全局对象有以下两点不同: 54 | 55 | - Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。 56 | 57 | - 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。 58 | 59 | 60 | 另外,通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,我们的代码将会变得更结构化且易维护。 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-analysis", 3 | "version": "1.0.0", 4 | "description": "analysis vue.js deeply", 5 | "directories": { 6 | "doc": "doc" 7 | }, 8 | "scripts": { 9 | "dev": "vuepress dev docs", 10 | "build": "vuepress build docs", 11 | "deploy": "sh build.sh" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/ustbhuangyi/vue-analysis.git" 16 | }, 17 | "keywords": [ 18 | "vue", 19 | "analysis" 20 | ], 21 | "author": "ustbuhuangyi", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/ustbhuangyi/vue-analysis/issues" 25 | }, 26 | "homepage": "https://github.com/ustbhuangyi/vue-analysis#readme", 27 | "devDependencies": { 28 | "vuepress": "^1.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /vue-router/build/release.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | echo "Enter release version: " 3 | read VERSION 4 | 5 | read -p "Releasing $VERSION - are you sure? (y/n)" -n 1 -r 6 | echo # (optional) move to a new line 7 | if [[ $REPLY =~ ^[Yy]$ ]] 8 | then 9 | echo "Releasing $VERSION ..." 10 | npm test 11 | VERSION=$VERSION npm run build 12 | 13 | # commit 14 | git add -A 15 | git commit -m "[build] $VERSION" 16 | npm version $VERSION --message "[release] $VERSION" 17 | 18 | # publish 19 | git push origin refs/tags/v$VERSION 20 | git push 21 | npm publish 22 | fi 23 | -------------------------------------------------------------------------------- /vue-router/build/rollup.dev.config.js: -------------------------------------------------------------------------------- 1 | const { input, output } = require('./configs')[0] 2 | 3 | module.exports = Object.assign({}, input, { output }) 4 | -------------------------------------------------------------------------------- /vue-router/build/update-docs.sh: -------------------------------------------------------------------------------- 1 | cd docs 2 | rm -rf _book 3 | gitbook install 4 | gitbook build 5 | cp assets/circle.yml _book/circle.yml 6 | cp assets/CNAME _book/CNAME 7 | cd _book 8 | git init 9 | git add -A 10 | git commit -m 'update book' 11 | git push -f git@github.com:vuejs/vue-router.git master:gh-pages 12 | -------------------------------------------------------------------------------- /vue-router/src/history/abstract.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import type Router from '../index' 4 | import { History } from './base' 5 | 6 | export class AbstractHistory extends History { 7 | index: number; 8 | stack: Array; 9 | 10 | constructor (router: Router, base: ?string) { 11 | super(router, base) 12 | this.stack = [] 13 | this.index = -1 14 | } 15 | 16 | push (location: RawLocation, onComplete?: Function, onAbort?: Function) { 17 | this.transitionTo(location, route => { 18 | this.stack = this.stack.slice(0, this.index + 1).concat(route) 19 | this.index++ 20 | onComplete && onComplete(route) 21 | }, onAbort) 22 | } 23 | 24 | replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { 25 | this.transitionTo(location, route => { 26 | this.stack = this.stack.slice(0, this.index).concat(route) 27 | onComplete && onComplete(route) 28 | }, onAbort) 29 | } 30 | 31 | go (n: number) { 32 | const targetIndex = this.index + n 33 | if (targetIndex < 0 || targetIndex >= this.stack.length) { 34 | return 35 | } 36 | const route = this.stack[targetIndex] 37 | this.confirmTransition(route, () => { 38 | this.index = targetIndex 39 | this.updateRoute(route) 40 | }) 41 | } 42 | 43 | getCurrentLocation () { 44 | const current = this.stack[this.stack.length - 1] 45 | return current ? current.fullPath : '/' 46 | } 47 | 48 | ensureURL () { 49 | // noop 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /vue-router/src/util/async.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export function runQueue (queue: Array, fn: Function, cb: Function) { 4 | const step = index => { 5 | if (index >= queue.length) { 6 | cb() 7 | } else { 8 | if (queue[index]) { 9 | fn(queue[index], () => { 10 | step(index + 1) 11 | }) 12 | } else { 13 | step(index + 1) 14 | } 15 | } 16 | } 17 | step(0) 18 | } 19 | -------------------------------------------------------------------------------- /vue-router/src/util/dom.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export const inBrowser = typeof window !== 'undefined' 4 | -------------------------------------------------------------------------------- /vue-router/src/util/params.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { warn } from './warn' 4 | import Regexp from 'path-to-regexp' 5 | 6 | // $flow-disable-line 7 | const regexpCompileCache: { 8 | [key: string]: Function 9 | } = Object.create(null) 10 | 11 | export function fillParams ( 12 | path: string, 13 | params: ?Object, 14 | routeMsg: string 15 | ): string { 16 | try { 17 | const filler = 18 | regexpCompileCache[path] || 19 | (regexpCompileCache[path] = Regexp.compile(path)) 20 | return filler(params || {}, { pretty: true }) 21 | } catch (e) { 22 | if (process.env.NODE_ENV !== 'production') { 23 | warn(false, `missing param for ${routeMsg}: ${e.message}`) 24 | } 25 | return '' 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /vue-router/src/util/warn.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export function assert (condition: any, message: string) { 4 | if (!condition) { 5 | throw new Error(`[vue-router] ${message}`) 6 | } 7 | } 8 | 9 | export function warn (condition: any, message: string) { 10 | if (process.env.NODE_ENV !== 'production' && !condition) { 11 | typeof console !== 'undefined' && console.warn(`[vue-router] ${message}`) 12 | } 13 | } 14 | 15 | export function isError (err: any): boolean { 16 | return Object.prototype.toString.call(err).indexOf('Error') > -1 17 | } 18 | -------------------------------------------------------------------------------- /vue-router/test/e2e/nightwatch.config.js: -------------------------------------------------------------------------------- 1 | // http://nightwatchjs.org/guide#settings-file 2 | 3 | module.exports = { 4 | 'src_folders': ['test/e2e/specs'], 5 | 'output_folder': 'test/e2e/reports', 6 | 'custom_commands_path': ['node_modules/nightwatch-helpers/commands'], 7 | 'custom_assertions_path': ['node_modules/nightwatch-helpers/assertions'], 8 | 9 | 'selenium': { 10 | 'start_process': true, 11 | 'server_path': require('selenium-server').path, 12 | 'host': '127.0.0.1', 13 | 'port': 4444, 14 | 'cli_args': { 15 | 'webdriver.chrome.driver': require('chromedriver').path 16 | } 17 | }, 18 | 19 | 'test_settings': { 20 | 'default': { 21 | 'selenium_port': 4444, 22 | 'selenium_host': 'localhost', 23 | 'silent': true, 24 | 'screenshots': { 25 | 'enabled': true, 26 | 'on_failure': true, 27 | 'on_error': false, 28 | 'path': 'test/e2e/screenshots' 29 | } 30 | }, 31 | 32 | 'chrome': { 33 | 'desiredCapabilities': { 34 | 'browserName': 'chrome', 35 | 'javascriptEnabled': true, 36 | 'acceptSslCerts': true, 37 | 'chromeOptions': { 38 | 'args': [ 39 | 'window-size=1280,800' 40 | ] 41 | } 42 | } 43 | }, 44 | 45 | 'phantomjs': { 46 | 'desiredCapabilities': { 47 | 'browserName': 'phantomjs', 48 | 'javascriptEnabled': true, 49 | 'acceptSslCerts': true 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /vue-router/test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var spawn = require('cross-spawn') 3 | var args = process.argv.slice(2) 4 | 5 | var server = args.indexOf('--dev') > -1 6 | ? null 7 | : require('../../examples/server') 8 | 9 | if (args.indexOf('--config') === -1) { 10 | args = args.concat(['--config', 'test/e2e/nightwatch.config.js']) 11 | } 12 | if (args.indexOf('--env') === -1) { 13 | args = args.concat(['--env', 'phantomjs']) 14 | } 15 | var i = args.indexOf('--test') 16 | if (i > -1) { 17 | args[i + 1] = 'test/e2e/specs/' + args[i + 1].replace(/\.js$/, '') + '.js' 18 | } 19 | if (args.indexOf('phantomjs') > -1) { 20 | process.env.PHANTOMJS = true 21 | } 22 | 23 | var runner = spawn('./node_modules/.bin/nightwatch', args, { 24 | stdio: 'inherit' 25 | }) 26 | 27 | runner.on('exit', function (code) { 28 | server && server.close() 29 | process.exit(code) 30 | }) 31 | 32 | runner.on('error', function (err) { 33 | server && server.close() 34 | throw err 35 | }) 36 | -------------------------------------------------------------------------------- /vue-router/test/e2e/specs/basic.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'basic': function (browser) { 3 | browser 4 | .url('http://localhost:8080/basic/') 5 | .waitForElementVisible('#app', 1000) 6 | .assert.count('li', 4) 7 | .assert.count('li a', 4) 8 | // assert correct href with base 9 | .assert.attributeContains('li:nth-child(1) a', 'href', '/basic/') 10 | .assert.attributeContains('li:nth-child(2) a', 'href', '/basic/foo') 11 | .assert.attributeContains('li:nth-child(3) a', 'href', '/basic/bar') 12 | .assert.attributeContains('li:nth-child(4) a', 'href', '/basic/bar') 13 | .assert.containsText('.view', 'home') 14 | 15 | .click('li:nth-child(2) a') 16 | .assert.urlEquals('http://localhost:8080/basic/foo') 17 | .assert.containsText('.view', 'foo') 18 | 19 | .click('li:nth-child(3) a') 20 | .assert.urlEquals('http://localhost:8080/basic/bar') 21 | .assert.containsText('.view', 'bar') 22 | 23 | .click('li:nth-child(1) a') 24 | .assert.urlEquals('http://localhost:8080/basic/') 25 | .assert.containsText('.view', 'home') 26 | 27 | .click('li:nth-child(4) a') 28 | .assert.urlEquals('http://localhost:8080/basic/bar') 29 | .assert.containsText('.view', 'bar') 30 | 31 | // check initial visit 32 | .url('http://localhost:8080/basic/foo') 33 | .waitForElementVisible('#app', 1000) 34 | .assert.containsText('.view', 'foo') 35 | .end() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /vue-router/test/e2e/specs/data-fetching.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'data fetching': function (browser) { 3 | browser 4 | .url('http://localhost:8080/data-fetching/') 5 | .waitForElementVisible('#app', 1000) 6 | .assert.count('li a', 4) 7 | .assert.containsText('.view', 'home') 8 | 9 | .click('li:nth-child(2) a') 10 | .waitForElementNotPresent('.loading', 500) 11 | .assert.containsText('.post h2', 'sunt aut facere') 12 | .assert.containsText('.post p', 'quia et suscipit') 13 | 14 | .click('li:nth-child(3) a') 15 | .waitForElementNotPresent('.loading', 500) 16 | .assert.containsText('.post h2', 'qui est esse') 17 | .assert.containsText('.post p', 'est rerum tempore') 18 | 19 | .click('li:nth-child(4) a') 20 | .waitForElementNotPresent('.loading', 500) 21 | .assert.elementNotPresent('.content') 22 | .assert.containsText('.error', 'Post not found') 23 | 24 | .click('li:nth-child(1) a') 25 | .assert.elementNotPresent('.post') 26 | .assert.containsText('.view', 'home') 27 | .end() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vue-router/test/e2e/specs/hash-mode.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'Hash mode': function (browser) { 3 | browser 4 | .url('http://localhost:8080/hash-mode/') 5 | .waitForElementVisible('#app', 1000) 6 | .assert.count('li', 4) 7 | .assert.count('li a', 3) 8 | .assert.attributeContains('li:nth-child(1) a', 'href', '/hash-mode/#/') 9 | .assert.attributeContains('li:nth-child(2) a', 'href', '/hash-mode/#/foo') 10 | .assert.attributeContains('li:nth-child(3) a', 'href', '/hash-mode/#/bar') 11 | .assert.containsText('.view', 'home') 12 | 13 | .click('li:nth-child(2) a') 14 | .assert.urlEquals('http://localhost:8080/hash-mode/#/foo') 15 | .assert.containsText('.view', 'foo') 16 | 17 | .click('li:nth-child(3) a') 18 | .assert.urlEquals('http://localhost:8080/hash-mode/#/bar') 19 | .assert.containsText('.view', 'bar') 20 | 21 | .click('li:nth-child(1) a') 22 | .assert.urlEquals('http://localhost:8080/hash-mode/#/') 23 | .assert.containsText('.view', 'home') 24 | 25 | .click('li:nth-child(4)') 26 | .assert.urlEquals('http://localhost:8080/hash-mode/#/bar') 27 | .assert.containsText('.view', 'bar') 28 | 29 | // check initial visit 30 | .url('http://localhost:8080/hash-mode/#/foo') 31 | .waitForElementVisible('#app', 1000) 32 | .assert.containsText('.view', 'foo') 33 | .end() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /vue-router/test/e2e/specs/lazy-loading.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'lazy loading': function (browser) { 3 | browser 4 | .url('http://localhost:8080/lazy-loading/') 5 | .waitForElementVisible('#app', 1000) 6 | .assert.count('li a', 4) 7 | .assert.containsText('.view', 'home') 8 | 9 | .click('li:nth-child(2) a') 10 | .assert.containsText('.view', 'This is Foo!') 11 | 12 | .click('li:nth-child(3) a') 13 | .assert.containsText('.view', 'This is Bar!') 14 | 15 | .click('li:nth-child(1) a') 16 | .assert.containsText('.view', 'home') 17 | 18 | .click('li:nth-child(4) a') 19 | .assert.containsText('.view', 'This is Bar!') 20 | .assert.containsText('.view h3', 'Baz') 21 | 22 | // test initial visit 23 | .url('http://localhost:8080/lazy-loading/foo') 24 | .waitForElementVisible('#app', 1000) 25 | .assert.containsText('.view', 'This is Foo!') 26 | 27 | .url('http://localhost:8080/lazy-loading/bar/baz') 28 | .waitForElementVisible('#app', 1000) 29 | .assert.containsText('.view', 'This is Bar!') 30 | .assert.containsText('.view h3', 'Baz') 31 | .end() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vue-router/test/e2e/specs/named-views.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'named views': function (browser) { 3 | browser 4 | .url('http://localhost:8080/named-views/') 5 | .waitForElementVisible('#app', 1000) 6 | .assert.count('li a', 2) 7 | // assert correct href with base 8 | .assert.attributeContains('li:nth-child(1) a', 'href', '/named-views/') 9 | .assert.attributeContains('li:nth-child(2) a', 'href', '/named-views/other') 10 | 11 | .assert.containsText('.view.one', 'foo') 12 | .assert.containsText('.view.two', 'bar') 13 | .assert.containsText('.view.three', 'baz') 14 | 15 | .click('li:nth-child(2) a') 16 | .assert.urlEquals('http://localhost:8080/named-views/other') 17 | .assert.containsText('.view.one', 'baz') 18 | .assert.containsText('.view.two', 'bar') 19 | .assert.containsText('.view.three', 'foo') 20 | 21 | .click('li:nth-child(1) a') 22 | .assert.urlEquals('http://localhost:8080/named-views/') 23 | .assert.containsText('.view.one', 'foo') 24 | .assert.containsText('.view.two', 'bar') 25 | .assert.containsText('.view.three', 'baz') 26 | 27 | // check initial visit 28 | .url('http://localhost:8080/named-views/other') 29 | .waitForElementVisible('#app', 1000) 30 | .assert.containsText('.view.one', 'baz') 31 | .assert.containsText('.view.two', 'bar') 32 | .assert.containsText('.view.three', 'foo') 33 | .end() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /vue-router/test/e2e/specs/nested-router.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'basic': function (browser) { 3 | browser 4 | .url('http://localhost:8080/nested-router/') 5 | .waitForElementVisible('#app', 1000) 6 | .assert.count('li a', 3) 7 | 8 | .click('li:nth-child(1) a') 9 | .assert.urlEquals('http://localhost:8080/nested-router/nested-router') 10 | .assert.containsText('.child', 'Child router path: /') 11 | .assert.count('li a', 5) 12 | 13 | .click('.child li:nth-child(1) a') 14 | .assert.containsText('.child', 'Child router path: /foo') 15 | .assert.containsText('.child .foo', 'foo') 16 | 17 | .click('.child li:nth-child(2) a') 18 | .assert.containsText('.child', 'Child router path: /bar') 19 | .assert.containsText('.child .bar', 'bar') 20 | 21 | .click('li:nth-child(2) a') 22 | .assert.urlEquals('http://localhost:8080/nested-router/foo') 23 | .assert.elementNotPresent('.child') 24 | .assert.containsText('#app', 'foo') 25 | .assert.count('li a', 3) 26 | .end() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /vue-router/test/e2e/specs/route-props.js: -------------------------------------------------------------------------------- 1 | const $attrs = ' { "foo": "123" }' 2 | 3 | module.exports = { 4 | 'route-props': function (browser) { 5 | browser 6 | .url('http://localhost:8080/route-props/') 7 | .waitForElementVisible('#app', 1000) 8 | .assert.count('li a', 5) 9 | 10 | .assert.urlEquals('http://localhost:8080/route-props/') 11 | .assert.containsText('.hello', 'Hello Vue!' + $attrs) 12 | 13 | .click('li:nth-child(2) a') 14 | .assert.urlEquals('http://localhost:8080/route-props/hello/you') 15 | .assert.containsText('.hello', 'Hello you' + $attrs) 16 | 17 | .click('li:nth-child(3) a') 18 | .assert.urlEquals('http://localhost:8080/route-props/static') 19 | .assert.containsText('.hello', 'Hello world' + $attrs) 20 | 21 | .click('li:nth-child(4) a') 22 | .assert.urlEquals('http://localhost:8080/route-props/dynamic/1') 23 | .assert.containsText('.hello', 'Hello ' + ((new Date()).getFullYear() + 1)+ '!' + $attrs) 24 | 25 | .click('li:nth-child(5) a') 26 | .assert.urlEquals('http://localhost:8080/route-props/attrs') 27 | .assert.containsText('.hello', 'Hello attrs' + $attrs) 28 | 29 | // should be consistent 30 | .click('li:nth-child(4) a') 31 | .click('li:nth-child(5) a') 32 | .assert.urlEquals('http://localhost:8080/route-props/attrs') 33 | .assert.containsText('.hello', 'Hello attrs' + $attrs) 34 | 35 | .end() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /vue-router/test/unit/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "test/unit/specs", 3 | "spec_files": [ 4 | "*.spec.js" 5 | ], 6 | "helpers": [ 7 | "../../../node_modules/babel-register/lib/node.js" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /vue-router/test/unit/specs/async.spec.js: -------------------------------------------------------------------------------- 1 | import { runQueue } from '../../../src/util/async' 2 | 3 | describe('Async utils', () => { 4 | describe('runQueue', () => { 5 | it('should work', done => { 6 | const calls = [] 7 | const queue = [1, 2, 3, 4, 5].map(i => next => { 8 | calls.push(i) 9 | setTimeout(next, 0) 10 | }) 11 | runQueue(queue, (fn, next) => fn(next), () => { 12 | expect(calls).toEqual([1, 2, 3, 4, 5]) 13 | done() 14 | }) 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /vue-router/test/unit/specs/create-matcher.spec.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable no-undef*/ 2 | import { createMatcher } from '../../../src/create-matcher' 3 | 4 | const routes = [ 5 | { path: '/', name: 'home', component: { name: 'home' }}, 6 | { path: '/foo', name: 'foo', component: { name: 'foo' }}, 7 | ] 8 | 9 | describe('Creating Matcher', function () { 10 | let match 11 | 12 | beforeAll(function () { 13 | spyOn(console, 'warn') 14 | match = createMatcher(routes).match 15 | }) 16 | 17 | beforeEach(function () { 18 | console.warn.calls.reset() 19 | process.env.NODE_ENV = 'production' 20 | }) 21 | 22 | it('in development, has logged a warning if a named route does not exist', function () { 23 | process.env.NODE_ENV = 'development' 24 | const { name, matched } = match({ name: 'bar' }, routes[0]) 25 | expect(matched.length).toBe(0) 26 | expect(name).toBe('bar') 27 | expect(console.warn).toHaveBeenCalled() 28 | expect(console.warn.calls.argsFor(0)[0]).toMatch('Route with name \'bar\' does not exist') 29 | }) 30 | 31 | it('in production, it has not logged this warning', function () { 32 | match({ name: 'foo' }, routes[0]) 33 | expect(console.warn).not.toHaveBeenCalled() 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /vue-router/test/unit/specs/custom-query.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from '../../../src/index' 3 | 4 | Vue.use(VueRouter) 5 | 6 | describe('custom query parse/stringify', () => { 7 | it('should work', () => { 8 | const router = new VueRouter({ 9 | parseQuery: () => ({ foo: 1 }), 10 | stringifyQuery: () => '?foo=1' 11 | }) 12 | 13 | router.push('/?bar=2') 14 | 15 | expect(router.currentRoute.query).toEqual({ foo: 1 }) 16 | expect(router.currentRoute.fullPath).toEqual('/?foo=1') 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /vue-router/test/unit/specs/discrete-components.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from '../../../src/index' 3 | 4 | describe('[Vue Instance].$route bindings', () => { 5 | describe('boundToSingleVueInstance', () => { 6 | it('updates $route on all instances', () => { 7 | const router = new VueRouter({ 8 | routes: [ 9 | { path: '/', component: { name: 'foo' }}, 10 | { path: '/bar', component: { name: 'bar' }} 11 | ] 12 | }) 13 | const app1 = new Vue({ router }) 14 | const app2 = new Vue({ router }) 15 | expect(app1.$route.path).toBe('/') 16 | expect(app2.$route.path).toBe('/') 17 | router.push('/bar') 18 | expect(app1.$route.path).toBe('/bar') 19 | expect(app2.$route.path).toBe('/bar') 20 | }) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /vue-router/test/unit/specs/node.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from '../../../src/index' 3 | 4 | Vue.use(VueRouter) 5 | 6 | describe('Usage in Node', () => { 7 | it('should be in abstract mode', () => { 8 | const router = new VueRouter() 9 | expect(router.mode).toBe('abstract') 10 | }) 11 | 12 | it('should be able to navigate without app instance', () => { 13 | const router = new VueRouter({ 14 | routes: [ 15 | { path: '/', component: { name: 'foo' }}, 16 | { path: '/bar', component: { name: 'bar' }} 17 | ] 18 | }) 19 | router.push('/bar') 20 | expect(router.history.current.path).toBe('/bar') 21 | }) 22 | 23 | it('getMatchedComponents', () => { 24 | const Foo = { name: 'foo' } 25 | const Bar = { name: 'bar' } 26 | const Baz = { name: 'baz' } 27 | const router = new VueRouter({ 28 | routes: [ 29 | { path: '/', component: Foo }, 30 | { path: '/bar', component: Bar, children: [ 31 | { path: 'baz', component: Baz } 32 | ]} 33 | ] 34 | }) 35 | expect(router.getMatchedComponents('/')).toEqual([Foo]) 36 | expect(router.getMatchedComponents('/bar/baz')).toEqual([Bar, Baz]) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /vue-router/test/unit/specs/query.spec.js: -------------------------------------------------------------------------------- 1 | import { resolveQuery, stringifyQuery } from '../../../src/util/query' 2 | 3 | describe('Query utils', () => { 4 | describe('resolveQuery', () => { 5 | it('should work', () => { 6 | const query = resolveQuery('foo=bar&foo=k', { baz: 'qux' }) 7 | expect(JSON.stringify(query)).toBe(JSON.stringify({ 8 | foo: ['bar', 'k'], 9 | baz: 'qux' 10 | })) 11 | }) 12 | }) 13 | 14 | describe('stringifyQuery', () => { 15 | it('should work', () => { 16 | expect(stringifyQuery({ 17 | foo: 'bar', 18 | baz: 'qux', 19 | arr: [1, 2] 20 | })).toBe('?foo=bar&baz=qux&arr=1&arr=2') 21 | }) 22 | 23 | it('should escape reserved chars', () => { 24 | expect(stringifyQuery({ 25 | a: '*()!' 26 | })).toBe('?a=%2a%28%29%21') 27 | }) 28 | 29 | it('should preserve commas', () => { 30 | expect(stringifyQuery({ 31 | list: '1,2,3' 32 | })).toBe('?list=1,2,3') 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | const ast = parse(template.trim(), options) 16 | if (options.optimize !== false) { 17 | optimize(ast, options) 18 | } 19 | const code = generate(ast, options) 20 | return { 21 | ast, 22 | render: code.render, 23 | staticRenderFns: code.staticRenderFns 24 | } 25 | }) 26 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/src/core/components/index.js: -------------------------------------------------------------------------------- 1 | import KeepAlive from './keep-alive' 2 | 3 | export default { 4 | KeepAlive 5 | } 6 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/src/core/observer/dep.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import type Watcher from './watcher' 4 | import { remove } from '../util/index' 5 | 6 | let uid = 0 7 | 8 | /** 9 | * A dep is an observable that can have multiple 10 | * directives subscribing to it. 11 | */ 12 | export default class Dep { 13 | static target: ?Watcher; 14 | id: number; 15 | subs: Array; 16 | 17 | constructor () { 18 | this.id = uid++ 19 | this.subs = [] 20 | } 21 | 22 | addSub (sub: Watcher) { 23 | this.subs.push(sub) 24 | } 25 | 26 | removeSub (sub: Watcher) { 27 | remove(this.subs, sub) 28 | } 29 | 30 | depend () { 31 | if (Dep.target) { 32 | Dep.target.addDep(this) 33 | } 34 | } 35 | 36 | notify () { 37 | // stabilize the subscriber list first 38 | const subs = this.subs.slice() 39 | for (let i = 0, l = subs.length; i < l; i++) { 40 | subs[i].update() 41 | } 42 | } 43 | } 44 | 45 | // the current target watcher being evaluated. 46 | // this is globally unique because there could be only one 47 | // watcher being evaluated at any time. 48 | Dep.target = null 49 | const targetStack = [] 50 | 51 | export function pushTarget (_target: ?Watcher) { 52 | if (Dep.target) targetStack.push(Dep.target) 53 | Dep.target = _target 54 | } 55 | 56 | export function popTarget () { 57 | Dep.target = targetStack.pop() 58 | } 59 | -------------------------------------------------------------------------------- /vue/src/core/observer/traverse.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { _Set as Set, isObject } from '../util/index' 4 | import type { SimpleSet } from '../util/index' 5 | import VNode from '../vdom/vnode' 6 | 7 | const seenObjects = new Set() 8 | 9 | /** 10 | * Recursively traverse an object to evoke all converted 11 | * getters, so that every nested property inside the object 12 | * is collected as a "deep" dependency. 13 | */ 14 | export function traverse (val: any) { 15 | _traverse(val, seenObjects) 16 | seenObjects.clear() 17 | } 18 | 19 | function _traverse (val: any, seen: SimpleSet) { 20 | let i, keys 21 | const isA = Array.isArray(val) 22 | if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) { 23 | return 24 | } 25 | if (val.__ob__) { 26 | const depId = val.__ob__.dep.id 27 | if (seen.has(depId)) { 28 | return 29 | } 30 | seen.add(depId) 31 | } 32 | if (isA) { 33 | i = val.length 34 | while (i--) _traverse(val[i], seen) 35 | } else { 36 | keys = Object.keys(val) 37 | i = keys.length 38 | while (i--) _traverse(val[keys[i]], seen) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/src/core/util/lang.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | /** 4 | * Check if a string starts with $ or _ 5 | */ 6 | export function isReserved (str: string): boolean { 7 | const c = (str + '').charCodeAt(0) 8 | return c === 0x24 || c === 0x5F 9 | } 10 | 11 | /** 12 | * Define a property. 13 | */ 14 | export function def (obj: Object, key: string, val: any, enumerable?: boolean) { 15 | Object.defineProperty(obj, key, { 16 | value: val, 17 | enumerable: !!enumerable, 18 | writable: true, 19 | configurable: true 20 | }) 21 | } 22 | 23 | /** 24 | * Parse simple path. 25 | */ 26 | const bailRE = /[^\w.$]/ 27 | export function parsePath (path: string): any { 28 | if (bailRE.test(path)) { 29 | return 30 | } 31 | const segments = path.split('.') 32 | return function (obj) { 33 | for (let i = 0; i < segments.length; i++) { 34 | if (!obj) return 35 | obj = obj[segments[i]] 36 | } 37 | return obj 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /vue/src/core/util/perf.js: -------------------------------------------------------------------------------- 1 | import { inBrowser } from './env' 2 | 3 | export let mark 4 | export let measure 5 | 6 | if (process.env.NODE_ENV !== 'production') { 7 | const perf = inBrowser && window.performance 8 | /* istanbul ignore if */ 9 | if ( 10 | perf && 11 | perf.mark && 12 | perf.measure && 13 | perf.clearMarks && 14 | perf.clearMeasures 15 | ) { 16 | mark = tag => perf.mark(tag) 17 | measure = (name, startTag, endTag) => { 18 | perf.measure(name, startTag, endTag) 19 | perf.clearMarks(startTag) 20 | perf.clearMarks(endTag) 21 | perf.clearMeasures(name) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/src/core/vdom/modules/ref.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { remove, isDef } from 'shared/util' 4 | 5 | export default { 6 | create (_: any, vnode: VNodeWithData) { 7 | registerRef(vnode) 8 | }, 9 | update (oldVnode: VNodeWithData, vnode: VNodeWithData) { 10 | if (oldVnode.data.ref !== vnode.data.ref) { 11 | registerRef(oldVnode, true) 12 | registerRef(vnode) 13 | } 14 | }, 15 | destroy (vnode: VNodeWithData) { 16 | registerRef(vnode, true) 17 | } 18 | } 19 | 20 | export function registerRef (vnode: VNodeWithData, isRemoval: ?boolean) { 21 | const key = vnode.data.ref 22 | if (!isDef(key)) return 23 | 24 | const vm = vnode.context 25 | const ref = vnode.componentInstance || vnode.elm 26 | const refs = vm.$refs 27 | if (isRemoval) { 28 | if (Array.isArray(refs[key])) { 29 | remove(refs[key], ref) 30 | } else if (refs[key] === ref) { 31 | refs[key] = undefined 32 | } 33 | } else { 34 | if (vnode.data.refInFor) { 35 | if (!Array.isArray(refs[key])) { 36 | refs[key] = [ref] 37 | } else if (refs[key].indexOf(ref) < 0) { 38 | // $flow-disable-line 39 | refs[key].push(ref) 40 | } 41 | } else { 42 | refs[key] = ref 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/src/platforms/web/entry-runtime.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import Vue from './runtime/index' 4 | 5 | export default Vue 6 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/src/platforms/web/runtime/modules/class.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { 4 | isDef, 5 | isUndef 6 | } from 'shared/util' 7 | 8 | import { 9 | concat, 10 | stringifyClass, 11 | genClassForVnode 12 | } from 'web/util/index' 13 | 14 | function updateClass (oldVnode: any, vnode: any) { 15 | const el = vnode.elm 16 | const data: VNodeData = vnode.data 17 | const oldData: VNodeData = oldVnode.data 18 | if ( 19 | isUndef(data.staticClass) && 20 | isUndef(data.class) && ( 21 | isUndef(oldData) || ( 22 | isUndef(oldData.staticClass) && 23 | isUndef(oldData.class) 24 | ) 25 | ) 26 | ) { 27 | return 28 | } 29 | 30 | let cls = genClassForVnode(vnode) 31 | 32 | // handle transition classes 33 | const transitionClass = el._transitionClasses 34 | if (isDef(transitionClass)) { 35 | cls = concat(cls, stringifyClass(transitionClass)) 36 | } 37 | 38 | // set the class 39 | if (cls !== el._prevClass) { 40 | el.setAttribute('class', cls) 41 | el._prevClass = cls 42 | } 43 | } 44 | 45 | export default { 46 | create: updateClass, 47 | update: updateClass 48 | } 49 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/src/platforms/web/runtime/patch.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import * as nodeOps from 'web/runtime/node-ops' 4 | import { createPatchFunction } from 'core/vdom/patch' 5 | import baseModules from 'core/vdom/modules/index' 6 | import platformModules from 'web/runtime/modules/index' 7 | 8 | // the directive module should be applied last, after all 9 | // built-in modules have been applied. 10 | const modules = platformModules.concat(baseModules) 11 | 12 | export const patch: Function = createPatchFunction({ nodeOps, modules }) 13 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/e2e/specs/basic-ssr.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
wtf
12 | 13 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/helpers/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "globals": { 6 | "waitForUpdate": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/ssr/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "plugins": ["jasmine"], 6 | "rules": { 7 | "jasmine/no-focused-tests": 2 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/ssr/fixtures/error.js: -------------------------------------------------------------------------------- 1 | throw new Error('foo') 2 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/ssr/fixtures/promise-rejection.js: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return Promise.reject(new Error('foo')) 3 | } 4 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/ssr/fixtures/test.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/vue/test/ssr/fixtures/test.css -------------------------------------------------------------------------------- /vue/test/ssr/fixtures/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/vue/test/ssr/fixtures/test.png -------------------------------------------------------------------------------- /vue/test/ssr/fixtures/test.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/vue/test/ssr/fixtures/test.woff2 -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/unit/modules/server-compiler/optimizer.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustbhuangyi/vue-analysis/813cfe1a8fe922203016eadeb3d8e1e70ac6ce47/vue/test/unit/modules/server-compiler/optimizer.spec.js -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "plugins": ["jasmine"], 6 | "rules": { 7 | "jasmine/no-focused-tests": 2 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/event/click.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/attrs.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | 24 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/classname.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | 25 | 37 | 38 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/components/banner.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/components/counter.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 37 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/components/editor.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | 32 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/components/footer.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/components/lifecycle.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 40 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/components/poster.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 22 | 23 | 34 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/components/stateful-lifecycle.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/components/stateful-v-model.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/components/stateful.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/components/stateless-multi-components.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 31 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/components/stateless-with-props.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/components/stateless.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/inline-style.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/text-node.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 23 | 24 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/v-else-if.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 23 | 24 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/v-else.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 22 | 23 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/v-for-iterator.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 25 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/v-for.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 23 | 24 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/v-if.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 22 | 23 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/v-on-inline.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 28 | 29 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/v-on.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 26 | 27 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/recycle-list/v-once.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/test/weex/cases/render/sample.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 24 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vue/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 | -------------------------------------------------------------------------------- /vuex/build/release.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | echo "Enter release version: " 3 | read VERSION 4 | 5 | read -p "Releasing $VERSION - are you sure? (y/n)" -n 1 -r 6 | echo # (optional) move to a new line 7 | if [[ $REPLY =~ ^[Yy]$ ]] 8 | then 9 | echo "Releasing $VERSION ..." 10 | 11 | # run tests 12 | npm test 2>/dev/null 13 | 14 | # build 15 | VERSION=$VERSION npm run build 16 | 17 | # commit 18 | git add -A 19 | git commit -m "[build] $VERSION" 20 | npm version $VERSION --message "[release] $VERSION" 21 | 22 | # publish 23 | git push origin refs/tags/v$VERSION 24 | git push 25 | npm publish 26 | fi 27 | -------------------------------------------------------------------------------- /vuex/build/rollup.dev.config.js: -------------------------------------------------------------------------------- 1 | const { input, output } = require('./configs').commonjs 2 | 3 | module.exports = Object.assign({}, input, { output }) 4 | -------------------------------------------------------------------------------- /vuex/build/rollup.logger.config.js: -------------------------------------------------------------------------------- 1 | const buble = require('rollup-plugin-buble') 2 | 3 | module.exports = { 4 | input: 'src/plugins/logger.js', 5 | output: { 6 | file: 'dist/logger.js', 7 | format: 'umd', 8 | name: 'createVuexLogger', 9 | }, 10 | plugins: [buble()] 11 | } 12 | -------------------------------------------------------------------------------- /vuex/dist/logger.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Types for the logger plugin. 3 | * This file must be put alongside the JavaScript file of the logger. 4 | */ 5 | 6 | import { Payload, Plugin } from "../types/index"; 7 | 8 | export interface LoggerOption { 9 | collapsed?: boolean; 10 | filter?:

(mutation: P, stateBefore: S, stateAfter: S) => boolean; 11 | transformer?: (state: S) => any; 12 | mutationTransformer?:

(mutation: P) => any; 13 | } 14 | 15 | export default function createLogger(option: LoggerOption): Plugin; 16 | -------------------------------------------------------------------------------- /vuex/src/index.esm.js: -------------------------------------------------------------------------------- 1 | import { Store, install } from './store' 2 | import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers' 3 | 4 | export default { 5 | Store, 6 | install, 7 | version: '__VERSION__', 8 | mapState, 9 | mapMutations, 10 | mapGetters, 11 | mapActions, 12 | createNamespacedHelpers 13 | } 14 | 15 | export { 16 | Store, 17 | install, 18 | mapState, 19 | mapMutations, 20 | mapGetters, 21 | mapActions, 22 | createNamespacedHelpers 23 | } 24 | -------------------------------------------------------------------------------- /vuex/src/index.js: -------------------------------------------------------------------------------- 1 | import { Store, install } from './store' 2 | import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers' 3 | 4 | export default { 5 | Store, 6 | install, 7 | version: '__VERSION__', 8 | mapState, 9 | mapMutations, 10 | mapGetters, 11 | mapActions, 12 | createNamespacedHelpers 13 | } 14 | -------------------------------------------------------------------------------- /vuex/src/mixin.js: -------------------------------------------------------------------------------- 1 | export default function (Vue) { 2 | const version = Number(Vue.version.split('.')[0]) 3 | 4 | if (version >= 2) { 5 | Vue.mixin({ beforeCreate: vuexInit }) 6 | } else { 7 | // override init and inject vuex init procedure 8 | // for 1.x backwards compatibility. 9 | const _init = Vue.prototype._init 10 | Vue.prototype._init = function (options = {}) { 11 | options.init = options.init 12 | ? [vuexInit].concat(options.init) 13 | : vuexInit 14 | _init.call(this, options) 15 | } 16 | } 17 | 18 | /** 19 | * Vuex init hook, injected into each instances init hooks list. 20 | */ 21 | 22 | function vuexInit () { 23 | const options = this.$options 24 | // store injection 25 | if (options.store) { 26 | this.$store = typeof options.store === 'function' 27 | ? options.store() 28 | : options.store 29 | } else if (options.parent && options.parent.$store) { 30 | this.$store = options.parent.$store 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vuex/src/plugins/devtool.js: -------------------------------------------------------------------------------- 1 | const devtoolHook = 2 | typeof window !== 'undefined' && 3 | window.__VUE_DEVTOOLS_GLOBAL_HOOK__ 4 | 5 | export default function devtoolPlugin (store) { 6 | if (!devtoolHook) return 7 | 8 | store._devtoolHook = devtoolHook 9 | 10 | devtoolHook.emit('vuex:init', store) 11 | 12 | devtoolHook.on('vuex:travel-to-state', targetState => { 13 | store.replaceState(targetState) 14 | }) 15 | 16 | store.subscribe((mutation, state) => { 17 | devtoolHook.emit('vuex:mutation', mutation, state) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /vuex/test/e2e/nightwatch.config.js: -------------------------------------------------------------------------------- 1 | // http://nightwatchjs.org/guide#settings-file 2 | module.exports = { 3 | 'src_folders': ['test/e2e/specs'], 4 | 'output_folder': 'test/e2e/reports', 5 | 'custom_commands_path': ['node_modules/nightwatch-helpers/commands'], 6 | 'custom_assertions_path': ['node_modules/nightwatch-helpers/assertions'], 7 | 8 | 'selenium': { 9 | 'start_process': true, 10 | 'server_path': require('selenium-server').path, 11 | 'host': '127.0.0.1', 12 | 'port': 4444, 13 | 'cli_args': { 14 | 'webdriver.chrome.driver': require('chromedriver').path 15 | } 16 | }, 17 | 18 | 'test_settings': { 19 | 'default': { 20 | 'selenium_port': 4444, 21 | 'selenium_host': 'localhost', 22 | 'silent': true, 23 | 'screenshots': { 24 | 'enabled': true, 25 | 'on_failure': true, 26 | 'on_error': false, 27 | 'path': 'test/e2e/screenshots' 28 | } 29 | }, 30 | 31 | 'chrome': { 32 | 'desiredCapabilities': { 33 | 'browserName': 'chrome', 34 | 'javascriptEnabled': true, 35 | 'acceptSslCerts': true 36 | } 37 | }, 38 | 39 | 'phantomjs': { 40 | 'desiredCapabilities': { 41 | 'browserName': 'phantomjs', 42 | 'javascriptEnabled': true, 43 | 'acceptSslCerts': true 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /vuex/test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | var spawn = require('cross-spawn') 2 | var args = process.argv.slice(2) 3 | 4 | var server = args.indexOf('--dev') > -1 5 | ? null 6 | : require('../../examples/server') 7 | 8 | if (args.indexOf('--config') === -1) { 9 | args = args.concat(['--config', 'test/e2e/nightwatch.config.js']) 10 | } 11 | if (args.indexOf('--env') === -1) { 12 | args = args.concat(['--env', 'phantomjs']) 13 | } 14 | var i = args.indexOf('--test') 15 | if (i > -1) { 16 | args[i + 1] = 'test/e2e/specs/' + args[i + 1] 17 | } 18 | if (args.indexOf('phantomjs') > -1) { 19 | process.env.PHANTOMJS = true 20 | } 21 | 22 | var runner = spawn('./node_modules/.bin/nightwatch', args, { 23 | stdio: 'inherit' 24 | }) 25 | 26 | runner.on('exit', function (code) { 27 | server && server.close() 28 | process.exit(code) 29 | }) 30 | 31 | runner.on('error', function (err) { 32 | server && server.close() 33 | throw err 34 | }) 35 | -------------------------------------------------------------------------------- /vuex/test/e2e/specs/cart.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'shopping cart': function (browser) { 3 | browser 4 | .url('http://localhost:8080/shopping-cart/') 5 | .waitForElementVisible('#app', 1000) 6 | .waitFor(120) // api simulation 7 | .assert.count('li', 3) 8 | .assert.count('.cart button[disabled]', 1) 9 | .assert.containsText('li:nth-child(1)', 'iPad 4 Mini') 10 | .assert.containsText('.cart', 'Please add some products to cart') 11 | .assert.containsText('.cart', 'Total: $0.00') 12 | .click('li:nth-child(1) button') 13 | .assert.containsText('.cart', 'iPad 4 Mini - $500.01 x 1') 14 | .assert.containsText('.cart', 'Total: $500.01') 15 | .click('li:nth-child(1) button') 16 | .assert.containsText('.cart', 'iPad 4 Mini - $500.01 x 2') 17 | .assert.containsText('.cart', 'Total: $1,000.02') 18 | .assert.count('li:nth-child(1) button[disabled]', 1) 19 | .click('li:nth-child(2) button') 20 | .assert.containsText('.cart', 'H&M T-Shirt White - $10.99 x 1') 21 | .assert.containsText('.cart', 'Total: $1,011.01') 22 | .click('.cart button') 23 | .waitFor(120) 24 | .assert.containsText('.cart', 'Please add some products to cart') 25 | .assert.containsText('.cart', 'Total: $0.00') 26 | .assert.containsText('.cart', 'Checkout successful') 27 | .assert.count('.cart button[disabled]', 1) 28 | .end() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /vuex/test/e2e/specs/counter.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'counter': function (browser) { 3 | browser 4 | .url('http://localhost:8080/counter/') 5 | .waitForElementVisible('#app', 1000) 6 | .assert.containsText('div', 'Clicked: 0 times') 7 | .click('button:nth-child(1)') 8 | .assert.containsText('div', 'Clicked: 1 times') 9 | .click('button:nth-child(2)') 10 | .assert.containsText('div', 'Clicked: 0 times') 11 | .click('button:nth-child(3)') 12 | .assert.containsText('div', 'Clicked: 0 times') 13 | .click('button:nth-child(1)') 14 | .assert.containsText('div', 'Clicked: 1 times') 15 | .click('button:nth-child(3)') 16 | .assert.containsText('div', 'Clicked: 2 times') 17 | .click('button:nth-child(4)') 18 | .assert.containsText('div', 'Clicked: 2 times') 19 | .waitFor(1000) 20 | .assert.containsText('div', 'Clicked: 3 times') 21 | .end() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /vuex/test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /vuex/test/unit/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "test/unit", 3 | "spec_files": [ 4 | "**/*.spec.js" 5 | ], 6 | "helpers": [ 7 | "../../node_modules/babel-register/lib/node.js", 8 | "setup.js" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /vuex/test/unit/setup.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill' 2 | import Vue from 'vue/dist/vue.common.js' 3 | import Vuex from '../../dist/vuex.common.js' 4 | 5 | Vue.use(Vuex) 6 | --------------------------------------------------------------------------------