| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- import Watcher from '../observer/watcher'
- import { extend, query, resolveAsset, hasOwn, isArray, isObject } from '../util/index'
- import { createElement, patch, updateListeners } from '../vdom/index'
- import { callHook } from './lifecycle'
- import { getPropValue } from './state'
- export const renderState = {
- activeInstance: null
- }
- export function initRender (vm) {
- vm._vnode = null
- vm._mounted = false
- vm.$slots = {}
- const el = vm.$options.el
- if (el) {
- vm.$mount(el)
- }
- }
- export function renderMixin (Vue) {
- // shorthands used in render functions
- Vue.prototype.__h__ = createElement
- Vue.prototype.__d__ = function (id) {
- return resolveAsset(this.$options, 'directives', id, true)
- }
- Vue.prototype._update = function (vnode) {
- if (this._mounted) {
- callHook(this, 'beforeUpdate')
- }
- if (!this._vnode) {
- this.$el = patch(this.$el, vnode)
- } else {
- this.$el = patch(this._vnode, vnode)
- }
- this._vnode = vnode
- if (this._mounted) {
- callHook(this, 'updated')
- }
- }
- Vue.prototype._tryUpdate = function (parentData, children, key) {
- const oldParentData = this.$options._renderData
- this.$options._renderKey = key
- this.$options._renderData = parentData
- this.$options._renderChildren = children
- // update props and listeners
- if (parentData) {
- updateProps(this, parentData)
- updateEvents(this, parentData)
- }
- // for now, if the component has content it always updates
- // because we don't know whether the children have changed.
- // need to optimize in the future.
- if (children || diffParentData(parentData, oldParentData)) {
- this.$forceUpdate()
- }
- }
- Vue.prototype._render = function () {
- const {
- render,
- _renderKey,
- _renderData,
- _renderChildren
- } = this.$options
- // resolve slots
- if (_renderChildren) {
- resolveSlots(this, _renderChildren)
- }
- // render
- const prev = renderState.activeInstance
- renderState.activeInstance = this
- const vnode = render.call(this)
- renderState.activeInstance = prev
- // set key
- vnode.key = _renderKey
- // update parent data
- if (_renderData) {
- mergeParentData(this, vnode.data, _renderData)
- }
- return vnode
- }
- Vue.prototype.$mount = function (el) {
- callHook(this, 'beforeMount')
- this.$el = el && query(el)
- if (this.$el) {
- this.$el.innerHTML = ''
- }
- this._watcher = new Watcher(this, this._render, this._update)
- this._update(this._watcher.value)
- callHook(this, 'mounted')
- this._mounted = true
- return this
- }
- Vue.prototype.$forceUpdate = function () {
- this._watcher.update()
- }
- }
- function resolveSlots (vm, children) {
- if (children) {
- children = children.slice()
- const slots = { default: children }
- let i = children.length
- let name, child
- while (i--) {
- child = children[i]
- if ((name = child.data && child.data.slot)) {
- let slot = (slots[name] || (slots[name] = []))
- if (child.tag === 'template') {
- slot.push.apply(slot, child.children)
- } else {
- slot.push(child)
- }
- children.splice(i, 1)
- }
- }
- vm.$slots = slots
- }
- }
- function diffParentData (data, oldData) {
- let key, old, cur
- for (key in oldData) {
- cur = data[key]
- old = oldData[key]
- if (key === 'on') continue
- if (!cur) return true
- if (isArray(old)) {
- if (!isArray(cur)) return true
- if (cur.length !== old.length) return true
- for (let i = 0; i < old.length; i++) {
- if (isObject(old[i])) {
- if (!isObject(cur[i])) return true
- if (diffObject(cur, old)) return true
- } else if (old[i] !== cur[i]) {
- return true
- }
- }
- } else if (diffObject(cur, old)) {
- return true
- }
- }
- }
- function diffObject (cur, old) {
- for (var key in old) {
- if (cur[key] !== old[key]) return true
- }
- }
- function mergeParentData (vm, data, parentData) {
- const props = vm.$options.props
- if (parentData.attrs) {
- const attrs = data.attrs || (data.attrs = {})
- for (let key in parentData.attrs) {
- if (!hasOwn(props, key)) {
- attrs[key] = parentData.attrs[key]
- }
- }
- }
- if (parentData.props) {
- const props = data.props || (data.props = {})
- for (let key in parentData.props) {
- if (!hasOwn(props, key)) {
- props[key] = parentData.props[key]
- }
- }
- }
- if (parentData.staticClass) {
- data.staticClass = data.staticClass
- ? data.staticClass + ' ' + parentData.staticClass
- : parentData.staticClass
- }
- if (parentData.class) {
- if (!data.class) {
- data.class = parentData.class
- } else {
- data.class = (isArray(data.class) ? data.class : []).concat(parentData.class)
- }
- }
- if (parentData.style) {
- if (!data.style) {
- data.style = parentData.style
- } else {
- extend(data.style, parentData.style)
- }
- }
- if (parentData.directives) {
- data.directives = parentData.directives.conact(data.directives || [])
- }
- }
- function updateProps (vm, data) {
- if (data.attrs || data.props) {
- for (let key in vm.$options.props) {
- vm[key] = getPropValue(data, key)
- }
- }
- }
- function updateEvents (vm, data) {
- if (data.on) {
- updateListeners(data.on, vm._vnode.data.on || {}, (event, handler) => {
- vm.$on(event, handler)
- })
- }
- }
|