Vue 2.0.0-alpha.1 源码阅读
2.0.0-alpha.1 源码目录如下:
vue 2.0.0-alpha.1 源码目录
📦vue 2.0.0-alpha.1
┣ 📂.git
┃ ┣ 📂branches
┃ ┣ 📂hooks
┃ ┃ ┣ 📜applypatch-msg.sample
┃ ┃ ┣ 📜commit-msg.sample
┃ ┃ ┣ 📜fsmonitor-watchman.sample
┃ ┃ ┣ 📜post-update.sample
┃ ┃ ┣ 📜pre-applypatch.sample
┃ ┃ ┣ 📜pre-commit.sample
┃ ┃ ┣ 📜pre-push.sample
┃ ┃ ┣ 📜pre-rebase.sample
┃ ┃ ┣ 📜pre-receive.sample
┃ ┃ ┣ 📜prepare-commit-msg.sample
┃ ┃ ┗ 📜update.sample
┃ ┣ 📂info
┃ ┃ ┗ 📜exclude
┃ ┣ 📂logs
┃ ┃ ┣ 📂refs
┃ ┃ ┃ ┣ 📂heads
┃ ┃ ┃ ┃ ┣ 📜main
┃ ┃ ┃ ┃ ┣ 📜tag-2.0.0
┃ ┃ ┃ ┃ ┗ 📜tag_0.6
┃ ┃ ┃ ┗ 📂remotes
┃ ┃ ┃ ┃ ┗ 📂origin
┃ ┃ ┃ ┃ ┃ ┗ 📜HEAD
┃ ┃ ┗ 📜HEAD
┃ ┣ 📂objects
┃ ┃ ┣ 📂info
┃ ┃ ┗ 📂pack
┃ ┃ ┃ ┣ 📜pack-03e96769417a03bc400fdad635674977e4f99a2b.idx
┃ ┃ ┃ ┗ 📜pack-03e96769417a03bc400fdad635674977e4f99a2b.pack
┃ ┣ 📂refs
┃ ┃ ┣ 📂heads
┃ ┃ ┃ ┣ 📜main
┃ ┃ ┃ ┣ 📜tag-2.0.0
┃ ┃ ┃ ┗ 📜tag_0.6
┃ ┃ ┣ 📂remotes
┃ ┃ ┃ ┗ 📂origin
┃ ┃ ┃ ┃ ┗ 📜HEAD
┃ ┃ ┗ 📂tags
┃ ┣ 📜HEAD
┃ ┣ 📜config
┃ ┣ 📜description
┃ ┣ 📜index
┃ ┗ 📜packed-refs
┣ 📂benchmarks
┃ ┣ 📂big-table
┃ ┃ ┣ 📜demo.css
┃ ┃ ┣ 📜index.html
┃ ┃ ┗ 📜style.css
┃ ┣ 📂dbmon
┃ ┃ ┣ 📂lib
┃ ┃ ┃ ┣ 📜bootstrap.min.css
┃ ┃ ┃ ┣ 📜memory-stats.js
┃ ┃ ┃ ┣ 📜monitor.js
┃ ┃ ┃ ┗ 📜styles.css
┃ ┃ ┣ 📜ENV.js
┃ ┃ ┣ 📜app.js
┃ ┃ ┗ 📜index.html
┃ ┣ 📂reorder-list
┃ ┃ ┗ 📜index.html
┃ ┣ 📂ssr
┃ ┃ ┣ 📜README.md
┃ ┃ ┣ 📜common.js
┃ ┃ ┣ 📜renderToStream.js
┃ ┃ ┗ 📜renderToString.js
┃ ┗ 📂svg
┃ ┃ ┗ 📜index.html
┣ 📂build
┃ ┣ 📜alias.js
┃ ┣ 📜build.js
┃ ┣ 📜ci.sh
┃ ┣ 📜karma.base.config.js
┃ ┣ 📜karma.cover.config.js
┃ ┣ 📜karma.dev.config.js
┃ ┣ 📜karma.sauce.config.js
┃ ┣ 📜karma.unit.config.js
┃ ┣ 📜nightwatch.config.js
┃ ┣ 📜release.sh
┃ ┣ 📜webpack.compiler.dev.config.js
┃ ┣ 📜webpack.dist.dev.config.js
┃ ┣ 📜webpack.dist.dev.entry.js
┃ ┣ 📜webpack.ssr.dev.config.js
┃ ┗ 📜webpack.ssr.dev.entry.js
┣ 📂dist
┃ ┣ 📜vue.common.js
┃ ┣ 📜vue.js
┃ ┣ 📜vue.js.map
┃ ┗ 📜vue.min.js
┣ 📂examples
┃ ┣ 📂commits
┃ ┃ ┣ 📜app.js
┃ ┃ ┗ 📜index.html
┃ ┣ 📂elastic-header
┃ ┃ ┣ 📜index.html
┃ ┃ ┗ 📜style.css
┃ ┣ 📂firebase
┃ ┃ ┣ 📜app.js
┃ ┃ ┣ 📜index.html
┃ ┃ ┗ 📜style.css
┃ ┣ 📂grid
┃ ┃ ┣ 📜grid.js
┃ ┃ ┣ 📜index.html
┃ ┃ ┗ 📜style.css
┃ ┣ 📂markdown
┃ ┃ ┣ 📜index.html
┃ ┃ ┗ 📜style.css
┃ ┣ 📂modal
┃ ┃ ┣ 📜index.html
┃ ┃ ┗ 📜style.css
┃ ┣ 📂select2
┃ ┃ ┗ 📜index.html
┃ ┣ 📂svg
┃ ┃ ┣ 📜index.html
┃ ┃ ┣ 📜style.css
┃ ┃ ┗ 📜svg.js
┃ ┣ 📂todomvc
┃ ┃ ┣ 📂js
┃ ┃ ┃ ┣ 📜app.js
┃ ┃ ┃ ┣ 📜routes.js
┃ ┃ ┃ ┗ 📜store.js
┃ ┃ ┣ 📂node_modules
┃ ┃ ┃ ┣ 📂director
┃ ┃ ┃ ┃ ┗ 📂build
┃ ┃ ┃ ┃ ┃ ┗ 📜director.js
┃ ┃ ┃ ┗ 📂todomvc-app-css
┃ ┃ ┃ ┃ ┗ 📜index.css
┃ ┃ ┣ 📜index.html
┃ ┃ ┣ 📜package.json
┃ ┃ ┣ 📜perf.js
┃ ┃ ┗ 📜readme.md
┃ ┗ 📂tree
┃ ┃ ┣ 📜index.html
┃ ┃ ┗ 📜tree.js
┣ 📂flow
┃ ┣ 📜compiler.js
┃ ┣ 📜component.js
┃ ┣ 📜global-api.js
┃ ┣ 📜options.js
┃ ┗ 📜vnode.js
┣ 📂packages
┃ ┣ 📂vue-server-renderer
┃ ┃ ┣ 📜index.js
┃ ┃ ┗ 📜package.json
┃ ┗ 📂vue-template-compiler
┃ ┃ ┣ 📜index.js
┃ ┃ ┗ 📜package.json
┣ 📂src
┃ ┣ 📂compiler
┃ ┃ ┣ 📂directives
┃ ┃ ┃ ┣ 📜bind.js
┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┗ 📜ref.js
┃ ┃ ┣ 📂parser
┃ ┃ ┃ ┣ 📜entity-decoder.js
┃ ┃ ┃ ┣ 📜filter-parser.js
┃ ┃ ┃ ┣ 📜html-parser.js
┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┣ 📜sfc-parser.js
┃ ┃ ┃ ┗ 📜text-parser.js
┃ ┃ ┣ 📜codegen.js
┃ ┃ ┣ 📜error-detector.js
┃ ┃ ┣ 📜events.js
┃ ┃ ┣ 📜helpers.js
┃ ┃ ┣ 📜index.js
┃ ┃ ┗ 📜optimizer.js
┃ ┣ 📂core
┃ ┃ ┣ 📂components
┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┗ 📜keep-alive.js
┃ ┃ ┣ 📂global-api
┃ ┃ ┃ ┣ 📜assets.js
┃ ┃ ┃ ┣ 📜extend.js
┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┣ 📜mixin.js
┃ ┃ ┃ ┗ 📜use.js
┃ ┃ ┣ 📂instance
┃ ┃ ┃ ┣ 📜events.js
┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┣ 📜init.js
┃ ┃ ┃ ┣ 📜lifecycle.js
┃ ┃ ┃ ┣ 📜proxy.js
┃ ┃ ┃ ┣ 📜render.js
┃ ┃ ┃ ┗ 📜state.js
┃ ┃ ┣ 📂observer
┃ ┃ ┃ ┣ 📜array.js
┃ ┃ ┃ ┣ 📜dep.js
┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┣ 📜scheduler.js
┃ ┃ ┃ ┗ 📜watcher.js
┃ ┃ ┣ 📂util
┃ ┃ ┃ ┣ 📜debug.js
┃ ┃ ┃ ┣ 📜env.js
┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┣ 📜lang.js
┃ ┃ ┃ ┣ 📜options.js
┃ ┃ ┃ ┗ 📜props.js
┃ ┃ ┣ 📂vdom
┃ ┃ ┃ ┣ 📂modules
┃ ┃ ┃ ┃ ┣ 📜directives.js
┃ ┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┃ ┗ 📜ref.js
┃ ┃ ┃ ┣ 📜create-component.js
┃ ┃ ┃ ┣ 📜create-element.js
┃ ┃ ┃ ┣ 📜helpers.js
┃ ┃ ┃ ┣ 📜patch.js
┃ ┃ ┃ ┗ 📜vnode.js
┃ ┃ ┣ 📜config.js
┃ ┃ ┗ 📜index.js
┃ ┣ 📂entries
┃ ┃ ┣ 📜web-compiler.js
┃ ┃ ┣ 📜web-runtime-with-compiler.js
┃ ┃ ┣ 📜web-runtime.js
┃ ┃ ┗ 📜web-server-renderer.js
┃ ┣ 📂platforms
┃ ┃ ┗ 📂web
┃ ┃ ┃ ┣ 📂compiler
┃ ┃ ┃ ┃ ┣ 📂directives
┃ ┃ ┃ ┃ ┃ ┣ 📜html.js
┃ ┃ ┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┃ ┃ ┣ 📜model.js
┃ ┃ ┃ ┃ ┃ ┗ 📜text.js
┃ ┃ ┃ ┃ ┣ 📂modules
┃ ┃ ┃ ┃ ┃ ┣ 📜class.js
┃ ┃ ┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┃ ┃ ┣ 📜style.js
┃ ┃ ┃ ┃ ┃ ┗ 📜transition.js
┃ ┃ ┃ ┃ ┗ 📜index.js
┃ ┃ ┃ ┣ 📂runtime
┃ ┃ ┃ ┃ ┣ 📂components
┃ ┃ ┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┃ ┃ ┗ 📜transition-control.js
┃ ┃ ┃ ┃ ┣ 📂directives
┃ ┃ ┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┃ ┃ ┣ 📜model.js
┃ ┃ ┃ ┃ ┃ ┗ 📜show.js
┃ ┃ ┃ ┃ ┣ 📂modules
┃ ┃ ┃ ┃ ┃ ┣ 📜attrs.js
┃ ┃ ┃ ┃ ┃ ┣ 📜class.js
┃ ┃ ┃ ┃ ┃ ┣ 📜events.js
┃ ┃ ┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┃ ┃ ┣ 📜props.js
┃ ┃ ┃ ┃ ┃ ┣ 📜style.js
┃ ┃ ┃ ┃ ┃ ┗ 📜transition.js
┃ ┃ ┃ ┃ ┣ 📜class-util.js
┃ ┃ ┃ ┃ ┣ 📜node-ops.js
┃ ┃ ┃ ┃ ┗ 📜patch.js
┃ ┃ ┃ ┣ 📂server
┃ ┃ ┃ ┃ ┣ 📂directives
┃ ┃ ┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┃ ┃ ┗ 📜show.js
┃ ┃ ┃ ┃ ┗ 📂modules
┃ ┃ ┃ ┃ ┃ ┣ 📜attrs.js
┃ ┃ ┃ ┃ ┃ ┣ 📜class.js
┃ ┃ ┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┃ ┃ ┗ 📜style.js
┃ ┃ ┃ ┗ 📂util
┃ ┃ ┃ ┃ ┣ 📜attrs.js
┃ ┃ ┃ ┃ ┣ 📜class.js
┃ ┃ ┃ ┃ ┣ 📜element.js
┃ ┃ ┃ ┃ ┗ 📜index.js
┃ ┣ 📂server
┃ ┃ ┣ 📜create-renderer.js
┃ ┃ ┣ 📜render-stream.js
┃ ┃ ┗ 📜render.js
┃ ┗ 📂shared
┃ ┃ ┗ 📜util.js
┣ 📂test
┃ ┣ 📂e2e
┃ ┃ ┣ 📂custom-assertions
┃ ┃ ┃ ┣ 📜attributePresent.js
┃ ┃ ┃ ┣ 📜checked.js
┃ ┃ ┃ ┣ 📜count.js
┃ ┃ ┃ ┣ 📜evaluate.js
┃ ┃ ┃ ┣ 📜focused.js
┃ ┃ ┃ ┣ 📜hasHTML.js
┃ ┃ ┃ ┗ 📜notVisible.js
┃ ┃ ┣ 📂custom-commands
┃ ┃ ┃ ┣ 📜dblClick.js
┃ ┃ ┃ ┗ 📜waitFor.js
┃ ┃ ┣ 📂specs
┃ ┃ ┃ ┣ 📜commits.js
┃ ┃ ┃ ┣ 📜grid.js
┃ ┃ ┃ ┣ 📜markdown.js
┃ ┃ ┃ ┣ 📜modal.js
┃ ┃ ┃ ┣ 📜select2.js
┃ ┃ ┃ ┣ 📜svg.js
┃ ┃ ┃ ┣ 📜todomvc.js
┃ ┃ ┃ ┗ 📜tree.js
┃ ┃ ┣ 📜.eslintrc
┃ ┃ ┗ 📜runner.js
┃ ┣ 📂helpers
┃ ┃ ┣ 📜.eslintrc
┃ ┃ ┣ 📜classlist.js
┃ ┃ ┣ 📜to-equal.js
┃ ┃ ┣ 📜to-have-been-warned.js
┃ ┃ ┣ 📜trigger-event.js
┃ ┃ ┣ 📜vdom.js
┃ ┃ ┗ 📜wait-for-update.js
┃ ┣ 📂ssr
┃ ┃ ┣ 📜.eslintrc
┃ ┃ ┣ 📜jasmine.json
┃ ┃ ┣ 📜ssr-env.spec.js
┃ ┃ ┣ 📜ssr-stream.spec.js
┃ ┃ ┗ 📜ssr-string.spec.js
┃ ┗ 📂unit
┃ ┃ ┣ 📂features
┃ ┃ ┃ ┣ 📂component
┃ ┃ ┃ ┃ ┣ 📜component-async.spec.js
┃ ┃ ┃ ┃ ┣ 📜component-keep-alive.spec.js
┃ ┃ ┃ ┃ ┣ 📜component-slot.spec.js
┃ ┃ ┃ ┃ ┗ 📜component.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-radio.spec.js
┃ ┃ ┃ ┃ ┣ 📜model-select.spec.js
┃ ┃ ┃ ┃ ┣ 📜model-text.spec.js
┃ ┃ ┃ ┃ ┣ 📜on.spec.js
┃ ┃ ┃ ┃ ┣ 📜once.spec.js
┃ ┃ ┃ ┃ ┣ 📜pre.spec.js
┃ ┃ ┃ ┃ ┣ 📜ref.spec.js
┃ ┃ ┃ ┃ ┣ 📜show.spec.js
┃ ┃ ┃ ┃ ┣ 📜style.spec.js
┃ ┃ ┃ ┃ ┗ 📜text.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
┃ ┃ ┃ ┃ ┣ 📜methods-events.spec.js
┃ ┃ ┃ ┃ ┣ 📜methods-lifecycle.spec.js
┃ ┃ ┃ ┃ ┣ 📜methods-watch.spec.js
┃ ┃ ┃ ┃ ┗ 📜properties.spec.js
┃ ┃ ┃ ┣ 📂options
┃ ┃ ┃ ┃ ┣ 📜components.spec.js
┃ ┃ ┃ ┃ ┣ 📜computed.spec.js
┃ ┃ ┃ ┃ ┣ 📜data.spec.js
┃ ┃ ┃ ┃ ┣ 📜delimiters.spec.js
┃ ┃ ┃ ┃ ┣ 📜directives.spec.js
┃ ┃ ┃ ┃ ┣ 📜el.spec.js
┃ ┃ ┃ ┃ ┣ 📜extends.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
┃ ┃ ┃ ┃ ┣ 📜template.spec.js
┃ ┃ ┃ ┃ ┗ 📜watch.spec.js
┃ ┃ ┃ ┣ 📂render
┃ ┃ ┃ ┃ ┗ 📜render.spec.js
┃ ┃ ┃ ┗ 📂transition
┃ ┃ ┃ ┃ ┣ 📜inject-styles.js
┃ ┃ ┃ ┃ ┣ 📜transition-mode.spec.js
┃ ┃ ┃ ┃ ┗ 📜transition.spec.js
┃ ┃ ┣ 📂modules
┃ ┃ ┃ ┣ 📂compiler
┃ ┃ ┃ ┃ ┣ 📜codegen.spec.js
┃ ┃ ┃ ┃ ┣ 📜optimizer.spec.js
┃ ┃ ┃ ┃ ┣ 📜parser.spec.js
┃ ┃ ┃ ┃ ┗ 📜sfc-parser.spec.js
┃ ┃ ┃ ┣ 📂observer
┃ ┃ ┃ ┃ ┣ 📜observer.spec.js
┃ ┃ ┃ ┃ ┣ 📜scheduler.spec.js
┃ ┃ ┃ ┃ ┗ 📜watcher.spec.js
┃ ┃ ┃ ┗ 📂vdom
┃ ┃ ┃ ┃ ┣ 📂modules
┃ ┃ ┃ ┃ ┃ ┣ 📜attrs.spec.js
┃ ┃ ┃ ┃ ┃ ┣ 📜class.spec.js
┃ ┃ ┃ ┃ ┃ ┣ 📜directive.spec.js
┃ ┃ ┃ ┃ ┃ ┣ 📜events.spec.js
┃ ┃ ┃ ┃ ┃ ┣ 📜props.spec.js
┃ ┃ ┃ ┃ ┃ ┗ 📜style.spec.js
┃ ┃ ┃ ┃ ┣ 📂patch
┃ ┃ ┃ ┃ ┃ ┣ 📜children.spec.js
┃ ┃ ┃ ┃ ┃ ┣ 📜element.spec.js
┃ ┃ ┃ ┃ ┃ ┣ 📜hooks.spec.js
┃ ┃ ┃ ┃ ┃ ┗ 📜hydration.spec.js
┃ ┃ ┃ ┃ ┣ 📜create-component.spec.js
┃ ┃ ┃ ┃ ┗ 📜create-element.spec.js
┃ ┃ ┣ 📜.eslintrc
┃ ┃ ┗ 📜index.js
┣ 📜.babelrc
┣ 📜.eslintignore
┣ 📜.eslintrc
┣ 📜.flowconfig
┣ 📜.gitignore
┣ 📜README.md
┣ 📜circle.yml
┣ 📜package-lock.json
┗ 📜package.json
入口文件
从源码的 package.json
中可以看到:
"scripts": {
"dev": "webpack --watch --config build/webpack.dist.dev.config.js"
}
dev 的入口文件在 build/webpack.dist.dev.config.js
,webpack 的一个配置文件,然后我们定位到这个文件会发现以下代码:
// 省去了部分代码
module.exports = {
entry: path.resolve(__dirname, 'webpack.dist.dev.entry.js')
}
entry
即入口文件,entry
配置的文件路径指向的文件为项目的入口文件,所以上面的代码 path.resolve(__dirname, 'webpack.dist.dev.entry.js')
表示我们的项目将从 build/webpack.dist.dev.entry.js
开始。
然后我们打开 build/webpack.dist.dev.entry.js
会发现以下代码:
module.exports = require('../src/entries/web-runtime-with-compiler')['default']
上述代码导出了 src/entries/web-runtime-with-compiler
,也就是说这个文件是我们正式的入口文件,下面我们就从该文件开始分析。
src/entries/web-runtime-with-compiler
/* @flow */
import Vue from './web-runtime'
import { warn, cached } from 'core/util/index'
import { query } from 'web/util/index'
import { compileToFunctions } from 'web/compiler/index'
const idToTemplate = cached(id => {
const el = query(id)
return el && el.innerHTML
})
// 缓存 web-runtime 版本的 $mount
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el)
// 获取实例化 vue 时的 options
const options = this.$options
// resolve template/el and convert to render function
// 判断 options 是否有 render
// 没有 render 的话,通过 compileToFunctions 将 template 转换成 render
// 然后再执行 web-runtime 版本的 $mount
if (!options.render) {
let template = options.template
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
}
} else if (template.nodeType) {
template = template.innerHTML
} else {
if (process.env.NODE_ENV !== 'production') {
warn('invalid template option:' + template, this)
}
return this
}
} else if (el) {
template = getOuterHTML(el)
}
if (template) {
const { render, staticRenderFns } = compileToFunctions(template, {
delimiters: options.delimiters,
warn
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
}
}
return mount.call(this, el, hydrating)
}
/**
* Get outerHTML of elements, taking care
* of SVG elements in IE as well.
*/
function getOuterHTML (el: Element): string {
if (el.outerHTML) {
return el.outerHTML
} else {
const container = document.createElement('div')
container.appendChild(el.cloneNode(true))
return container.innerHTML
}
}
Vue.compile = compileToFunctions
export default Vue
上述代码的逻辑基本上也很简单,就是引入 web-runtime
版本的 vue 后缓存 $mount
方法
const mount = Vue.prototype.$mount
然后重写 Vue.prototype.$mount
:
Vue.prototype.$mount = function () {}
在重写的方法中先判断了实例化的 vue 中是否传入了 render
Vue.prototype.$mount = function () {
if (!options.render) {}
return mount.call(this, el, hydrating)
}
如果没有传入的话则判断实例化 vue 时时候传入了 templete,有的话则通过一系列方法获取到 templete 的内容,没有的话则获取到实例化 vue 时传入的 el 外部的 html 为 templete,最后通过 compileToFunctions
方法转换为 render
后再调用缓存的 $mount
方法挂载。
// 判断实例化 vue 时是否传入 render
if (!options.render) {
// 没有的话通过一些列方法将 templete 转成 render
let template = options.template
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
}
} else if (template.nodeType) {
template = template.innerHTML
} else {
if (process.env.NODE_ENV !== 'production') {
warn('invalid template option:' + template, this)
}
return this
}
} else if (el) {
template = getOuterHTML(el)
}
if (template) {
const { render, staticRenderFns } = compileToFunctions(template, {
delimiters: options.delimiters,
warn
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
}
}
总结而言,入口文件除了导出 vue 文件外,还重写了来自 web-runtime 的 mount 方法,其他的就没有了,接下来我们定位到 web-runtime 文件看看有什么!
web-runtime.js
src/entries/web-runtime.js
/* @flow */
import Vue from 'core/index'
import config from 'core/config'
import { extend, noop } from 'shared/util'
import { patch } from 'web/runtime/patch'
import platformDirectives from 'web/runtime/directives/index'
import platformComponents from 'web/runtime/components/index'
import { query, isUnknownElement, isReservedTag, mustUseProp } from 'web/util/index'
// install platform specific utils
Vue.config.isUnknownElement = isUnknownElement
Vue.config.isReservedTag = isReservedTag
Vue.config.mustUseProp = mustUseProp
// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)
// install platform patch function
Vue.prototype.__patch__ = config._isServer ? noop : patch
// wrap mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
return this._mount(el && query(el), hydrating)
}
export default Vue
打开这个文件会发现 vue 的核心代码仍然不在这里,web-runtime.js 可以看到是引入了核心的 vue 模块,然后扩展了 vue 的一些 directives
和 components
后导出了 vue,所以我们继续查看来自 core/index
的代码。
src/core/index.js
import config from './config'
import { initGlobalAPI } from './global-api/index'
import Vue from './instance/index'
initGlobalAPI(Vue)
Object.defineProperty(Vue.prototype, '$isServer', {
get: () => config._isServer
})
Vue.version = '2.0.0-alpha.1'
export default Vue
从上面的代码可以看到这个文件主要的作用是初始化一些 vue 的全局 api 然后导出 vue,但是到这里也不是 vue 核心文件,所以我们继续向上查找。
src/core/instance/index.js
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
function Vue (options) {
this._init(options)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
非常激动在这里终于看到定义 Vue 的地方了!但是一看这里的代码也是很简洁,就是定义了 Vue 的构造函数,然后把 Vue 传入不同的函数去给 Vue 的构造函数原型上添加不同的方法,在执行 new Vue
的时候调用原型上的 this._init
方法。
initMixin 方法
上方代码中 this._init()
方法就是通过 initMixin
挂载的,所有接下来看看这个方法。
src/core/instance/init.js
/* @flow */
import { initProxy } from './proxy'
import { initState } from './state'
import { initRender } from './render'
import { initEvents } from './events'
import { initLifecycle, callHook } from './lifecycle'
import { mergeOptions } from '../util/index'
let uid = 0
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
vm._uid = uid++
// a flag to avoid this being observed
vm._isVue = true
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
vm.constructor.options,
options || {},
vm
)
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
callHook(vm, 'init')
initState(vm)
callHook(vm, 'created')
initRender(vm)
}
}
function initInternalComponent (vm: Component, options: InternalComponentOptions) {
const opts = vm.$options = Object.create(vm.constructor.options)
// doing this because it's faster than dynamic enumeration.
opts.parent = options.parent
opts.propsData = options.propsData
opts._parentVnode = options._parentVnode
opts._parentListeners = options._parentListeners
opts._renderChildren = options._renderChildren
opts._componentTag = options._componentTag
if (options.render) {
opts.render = options.render
opts.staticRenderFns = opts.staticRenderFns
}
}
下面我们一步步分析 initMixin
函数代码~
可以看到 只做了一件事就是在 Vue 的原型上挂载了 _init
方法:Vue.prototype._init = function (options?: Object) {}
然后我们一步步看看 Vue.prototype._init
:
Vue.prototype._init = function (options?: Object) {
// 获取 vue 的实例
const vm: Component = this
// 每一个组件都会有一个 uid,确保组件的唯一性
vm._uid = uid++
// 这里是一个标记,用来告诉监听者 vm 不需要响应式,后续会提到
vm._isVue = true
// 合并外部传入的 options
// 判断是否是独立的组件,如果是的话特殊处理
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
vm.constructor.options,
options || {},
vm
)
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
callHook(vm, 'init')
initState(vm)
callHook(vm, 'created')
initRender(vm)
}