前言:前面我们根据vue官网的生命周期图走了一遍vue的源码,感兴趣的小伙伴可以去看我之前的文章Vue源码解析(一),今天我们重点研究一下render跟mount方法,这也是vue中两个比较重要的方法了,废话不多说了,我们开撸~~
我们先回顾下上节内容,我们打开vue的源码,然后找到/vue/src/core/instance/init.js:
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// 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(
resolveConstructorOptions(vm.constructor),
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)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
可以说这段代码就是贯穿了vue的整个生命周期,我们可以看到最后一行代码:
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
当我们的vue实例中有el属性的时候就会直接走vm的mount方法,那么如果我们不提供el属性的时候,我们该怎么走呢?
我们创建一个简单的vue页面:
page-a.vue:
然后我们在page-a.vue中引用一个叫Render3.js的组件:
export default {
name: 'Render3',
render() {
return 我是render3
}
}
可以看到,我们直接用js文件定义了一个对象,对象里面有name属性跟render方法,render方法我们待会再分析,那么我们怎么才能page-a.vue中用起来呢?
方法一:
mounted() {
//继承vue的属性
let Render3Class=this.$options._base.extend(Render3)
//创建一个vue实例
let render3VM=new Render3Class({
//创建一个空元素
el: document.createElement('div')
})
//把render3VM的el添加到当前vue的el中
this.$el.appendChild(render3VM.$el)
},
我们还可以直接把组件注册为局部组件或者全局组件:
第二种我就不介绍了哈,小伙伴都会用,我们看一下第一种方式:
import Render3 from '../components/Render3'
export default {
name: 'pageA',
mounted() {
let Render3Class=this.$options._base.extend(Render3)
let render3VM=new Render3Class({
el: document.createElement('div')
})
this.$el.appendChild(render3VM.$el)
},
methods: {
onClick() {
this.$store.dispatch('login')
}
}
我们前面知道,当提供了el属性给vm的时候,直接会走mount方法,我们把el属性注释掉试试:
mounted() {
let Render3Class=this.$options._base.extend(Render3)
let render3VM=new Render3Class({
// el: document.createElement('div')
})
this.$el.appendChild(render3VM.$el)
},
可以看到,render3没有被渲染,那我们怎么办呢? 我们可以自己调用一下:
mounted() {
let Render3Class=this.$options._base.extend(Render3)
let render3VM=new Render3Class({
// el: document.createElement('div')
})
render3VM.$mount(document.createElement('div'))
this.$el.appendChild(render3VM.$el)
},
我就不截图了哈,效果跟之前一样,我们来分析一下mount方法,mount方法字面上意思就是“渲染”的意思,当我们直接执行:
render3VM.$mount(document.createElement('div'))
方法的时候,我们首先走了/vue/src/platforms/web/entry-runtime-with-compiler.js:
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
//在vm中传递的el属性
el = el && query(el)
/* istanbul ignore if */
if (el === document.body || el === document.documentElement) {
process.env.NODE_ENV !== 'production' && warn(
`Do not mount Vue to or - mount to normal elements instead.`
)
return this
}
const options = this.$options
// 如果在options中没有传递render方法
if (!options.render) {
let template = options.template
//如果有template属性,就把template编译完毕后传递给render方法
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !template) {
warn(
`Template element not found or is empty: ${options.template}`,
this
)
}
}
} 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属性但是有el属性的时候,把el的outerHtml当成template传递给render对象
template = getOuterHTML(el)
}
if (template) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile')
}
const { render, staticRenderFns } = compileToFunctions(template, {
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile end')
measure(`vue ${this._name} compile`, 'compile', 'compile end')
}
}
}
return mount.call(this, el, hydrating)
}
然后会走vue/src/platforms/web/runtime/index.js:
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
最后会走vue/src/core/instance/lifecycle.js:
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
if (process.env.NODE_ENV !== 'production') {
/* istanbul ignore if */
if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
vm.$options.el || el) {
warn(
'You are using the runtime-only build of Vue where the template ' +
'compiler is not available. Either pre-compile the templates into ' +
'render functions, or use the compiler-included build.',
vm
)
} else {
warn(
'Failed to mount component: template or render function not defined.',
vm
)
}
}
}
callHook(vm, 'beforeMount')
let updateComponent
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
updateComponent = () => {
const name = vm._name
const id = vm._uid
const startTag = `vue-perf-start:${id}`
const endTag = `vue-perf-end:${id}`
mark(startTag)
const vnode = vm._render()
mark(endTag)
measure(`vue ${name} render`, startTag, endTag)
mark(startTag)
vm._update(vnode, hydrating)
mark(endTag)
measure(`vue ${name} patch`, startTag, endTag)
}
} else {
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
}
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)
hydrating = false
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
可以看到,最后执行了回调了mounted事件,我们组件中的mounted方法被执行,在第一行中我们可以看到:
vm.$el = el
所以也是为什么我们在beforecreated跟created方法中拿不到this. e l 的 原 因 , 因 为 t h i s . el的原因,因为this. el的原因,因为this.el还没被赋值.
mount方法的整个流程我们简单的走了一遍了,我们再反过来一起研究一下render方法,render基本用法我就不解释了,官网比我说的好,小伙伴可以自己看官网哈,
https://cn.vuejs.org/v2/guide/render-function.html
我们重点研究一下render方法的源码
小伙伴有没有发现,在vue文件中如果提供了render方法,render方法不执行?
这其实跟vue-loader有关,当vue-loader在加载.vue文件的时候,当发现.vue文件中有template标签的时候,就会把template解析完毕后然后创建render方法,替换当前vm的render方法,所以我们在.vue文件中提供的render方法其实是被覆盖掉了,所以如果在.vue文件中使用render方法的时候,我们需要去掉template标签.
像这样的:
vue-loader的源码我这就不解析了,不然写不完了,哈哈~~
好啦,又说了一段废话,我们还是快进入我们的主题哈
我们继续看这个文件中的mount方法vue/src/platforms/web/entry-runtime-with-compiler.js:
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
//在vm中传递的el属性
el = el && query(el)
/* istanbul ignore if */
if (el === document.body || el === document.documentElement) {
process.env.NODE_ENV !== 'production' && warn(
`Do not mount Vue to or - mount to normal elements instead.`
)
return this
}
const options = this.$options
// 如果在options中没有传递render方法
if (!options.render) {
let template = options.template
//如果有template属性,就把template编译完毕后传递给render方法
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !template) {
warn(
`Template element not found or is empty: ${options.template}`,
this
)
}
}
} 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属性但是有el属性的时候,把el的outerHtml当成template传递给render对象
template = getOuterHTML(el)
}
if (template) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile')
}
const { render, staticRenderFns } = compileToFunctions(template, {
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile end')
measure(`vue ${this._name} compile`, 'compile', 'compile end')
}
}
}
return mount.call(this, el, hydrating)
}
我们刚也说了,当.vue文件的时候vue-loader会判断template标签,然后创建render方法,所以我们在这的时候默认认为是有render方法的,那如果.vue中没有template标签也没有提供render方法的话该怎么走呢?
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
if (process.env.NODE_ENV !== 'production') {
/* istanbul ignore if */
if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
vm.$options.el || el) {
warn(
'You are using the runtime-only build of Vue where the template ' +
'compiler is not available. Either pre-compile the templates into ' +
'render functions, or use the compiler-included build.',
vm
)
} else {
warn(
'Failed to mount component: template or render function not defined.',
vm
)
}
}
}
在mountComponent方法中我们看到,如果我们没有提供render方法的时候,vue就会自动给我们创建一个空的vnode对象:
vm.$options.render = createEmptyVNode
export const createEmptyVNode = (text: string = '') => {
const node = new VNode()
node.text = text
node.isComment = true
return node
}
我们还是继续回到这个文件哈
/vue/src/platforms/web/entry-runtime-with-compiler.js:
if (!options.render) {
let template = options.template
//如果有template属性,就把template编译完毕后传递给render方法
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !template) {
warn(
`Template element not found or is empty: ${options.template}`,
this
)
}
}
} 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属性但是有el属性的时候,把el的outerHtml当成template传递给render对象
template = getOuterHTML(el)
}
if (template) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile')
}
const { render, staticRenderFns } = compileToFunctions(template, {
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile end')
measure(`vue ${this._name} compile`, 'compile', 'compile end')
}
}
当判断有template的时候,我们就走了下面代码:
if (template) {
...
const { render, staticRenderFns } = compileToFunctions(template, {
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
....
}
我们可以看到当有template的时候,我们走了compileToFunctions方法,然后生成了render方法,我们看一下compileToFunctions方法,vue/src/platforms/web/compiler/index.js:
import { baseOptions } from './options'
import { createCompiler } from 'compiler/index'
const { compile, compileToFunctions } = createCompiler(baseOptions)
export { compile, compileToFunctions }
然后compileToFunctions方法我们可以看到在这定义的
vue/src/compiler/index.js:
export const createCompiler = createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
optimize(ast, options)
}
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
然后我们可以看到generate方法,vue/src/compiler/codegen/index.js:
export function generate (
ast: ASTElement | void,
options: CompilerOptions
): CodegenResult {
const state = new CodegenState(options)
const code = ast ? genElement(ast, state) : '_c("div")'
return {
render: `with(this){return ${code}}`,
staticRenderFns: state.staticRenderFns
}
}
可以看到,返回的结果中render为with(this){return _c(“div”)}或者是由genElement方法返回的code字符串:
export function genElement (el: ASTElement, state: CodegenState): string {
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
return genOnce(el, state)
} else if (el.for && !el.forProcessed) {
return genFor(el, state)
} else if (el.if && !el.ifProcessed) {
return genIf(el, state)
} else if (el.tag === 'template' && !el.slotTarget) {
return genChildren(el, state) || 'void 0'
} else if (el.tag === 'slot') {
return genSlot(el, state)
} else {
// component or element
let code
if (el.component) {
code = genComponent(el.component, el, state)
} else {
const data = el.plain ? undefined : genData(el, state)
const children = el.inlineTemplate ? null : genChildren(el, state, true)
code = `_c('${el.tag}'${
data ? `,${data}` : '' // data
}${
children ? `,${children}` : '' // children
})`
}
// module transforms
for (let i = 0; i < state.transforms.length; i++) {
code = state.transforms[i](el, code)
}
return code
}
}
我们可以看到有genStatic、genOnce、genFor等等…
// hoist static sub-trees out
function genStatic (el: ASTElement, state: CodegenState): string {
el.staticProcessed = true
state.staticRenderFns.push(`with(this){return ${genElement(el, state)}}`)
return `_m(${
state.staticRenderFns.length - 1
}${
el.staticInFor ? ',true' : ''
})`
}
所以最后返回的就是“_m()、_o()”这样的~~
好啦,看完这个我们可能会疑惑,为什么render方法是字符串呢? 不应该是返回的方法吗? 我们继续往后看哈~
vue/src/compiler/index.js:
/* @flow */
import { parse } from './parser/index'
import { optimize } from './optimizer'
import { generate } from './codegen/index'
import { createCompilerCreator } from './create-compiler'
// `createCompilerCreator` allows creating compilers that use alternative
// parser/optimizer/codegen, e.g the SSR optimizing compiler.
// Here we just export a default compiler using the default parts.
export const createCompiler = createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
optimize(ast, options)
}
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
然后我们看createCompilerCreator方法,vue/src/compiler/create-compiler.js:
import { extend } from 'shared/util'
import { detectErrors } from './error-detector'
import { createCompileToFunctionFn } from './to-function'
export function createCompilerCreator (baseCompile: Function): Function {
return function createCompiler (baseOptions: CompilerOptions) {
function compile (
template: string,
options?: CompilerOptions
): CompiledResult {
....
return {
compile,
compileToFunctions: createCompileToFunctionFn(compile)
}
}
}
createCompileToFunctionFn方法又是啥呢?vue/src/compiler/to-function.js:
export function createCompileToFunctionFn (compile: Function): Function {
const cache = Object.create(null)
return function compileToFunctions (
template: string,
options?: CompilerOptions,
vm?: Component
): CompiledFunctionResult {
...
res.render = createFunction(compiled.render, fnGenErrors)
res.staticRenderFns = compiled.staticRenderFns.map(code => {
return createFunction(code, fnGenErrors)
})
....
}
}
function createFunction (code, errors) {
try {
return new Function(code)
} catch (err) {
errors.push({ err, code })
return noop
}
}
好吧,终于是看到庐山真面目了,我们返回的"_c()、_o()"字符串最后通过new Function(code)变成了Function对象,那么_c()、_o()方法又是啥呢?
vue/src/core/instance/render.js:
export function initRender (vm: Component) {
...
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
...
export function renderMixin (Vue: Class) {
// install runtime convenience helpers
installRenderHelpers(Vue.prototype)
.....
}
vue/src/core/instance/render-helpers/index.js:
/* @flow */
import { toNumber, toString, looseEqual, looseIndexOf } from 'shared/util'
import { createTextVNode, createEmptyVNode } from 'core/vdom/vnode'
import { renderList } from './render-list'
import { renderSlot } from './render-slot'
import { resolveFilter } from './resolve-filter'
import { checkKeyCodes } from './check-keycodes'
import { bindObjectProps } from './bind-object-props'
import { renderStatic, markOnce } from './render-static'
import { bindObjectListeners } from './bind-object-listeners'
import { resolveScopedSlots } from './resolve-slots'
export function installRenderHelpers (target: any) {
target._o = markOnce
target._n = toNumber
target._s = toString
target._l = renderList
target._t = renderSlot
target._q = looseEqual
target._i = looseIndexOf
target._m = renderStatic
target._f = resolveFilter
target._k = checkKeyCodes
target._b = bindObjectProps
target._v = createTextVNode
target._e = createEmptyVNode
target._u = resolveScopedSlots
target._g = bindObjectListeners
}
可以看到,不只有_c、_o,还有方法~,再往下就是根据标签跟属性创建对应的vnode对象了,所以整个render对象看完我们可以知道,render对象最后就是返回了一个vnode对象.
我们回到我们的mount方法,vue/src/core/instance/lifecycle.js:
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
if (process.env.NODE_ENV !== 'production') {
/* istanbul ignore if */
if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
vm.$options.el || el) {
warn(
'You are using the runtime-only build of Vue where the template ' +
'compiler is not available. Either pre-compile the templates into ' +
'render functions, or use the compiler-included build.',
vm
)
} else {
warn(
'Failed to mount component: template or render function not defined.',
vm
)
}
}
}
callHook(vm, 'beforeMount')
let updateComponent
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
updateComponent = () => {
const name = vm._name
const id = vm._uid
const startTag = `vue-perf-start:${id}`
const endTag = `vue-perf-end:${id}`
mark(startTag)
const vnode = vm._render()
mark(endTag)
measure(`vue ${name} render`, startTag, endTag)
mark(startTag)
vm._update(vnode, hydrating)
mark(endTag)
measure(`vue ${name} patch`, startTag, endTag)
}
} else {
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
}
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)
hydrating = false
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}
可以看到,最重要的就是创建了一个Watcher对象:
new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)
然后传入了updateComponent方法:
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
Watcher先介绍一下,Watcher对象可以监听响应式数据的变化,然后Watcher对象中会判断需不需要刷新组件,触发updateComponent方法,然后触发vm的_update方法,最后更新dom~~
所以我们看一下_update干了什么?
vue/src/core/instance/lifecycle.js
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
const vm: Component = this
if (vm._isMounted) {
callHook(vm, 'beforeUpdate')
}
const prevEl = vm.$el
const prevVnode = vm._vnode
const prevActiveInstance = activeInstance
activeInstance = vm
vm._vnode = vnode
// Vue.prototype.__patch__ is injected in entry points
// based on the rendering backend used.
if (!prevVnode) {
// initial render
vm.$el = vm.__patch__(
vm.$el, vnode, hydrating, false /* removeOnly */,
vm.$options._parentElm,
vm.$options._refElm
)
// no need for the ref nodes after initial patch
// this prevents keeping a detached DOM tree in memory (#5851)
vm.$options._parentElm = vm.$options._refElm = null
} else {
// updates
vm.$el = vm.__patch__(prevVnode, vnode)
}
activeInstance = prevActiveInstance
// update __vue__ reference
if (prevEl) {
prevEl.__vue__ = null
}
if (vm.$el) {
vm.$el.__vue__ = vm
}
// if parent is an HOC, update its $el as well
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
vm.$parent.$el = vm.$el
}
// updated hook is called by the scheduler to ensure that children are
// updated in a parent's updated hook.
}
可以看到,update方法中接受到了vnode对象后,最后通过下面代码转成了dom对象:
vm.$el = vm.__patch__(prevVnode, vnode)
那么__patch__方法又是啥呢?vue/src/platforms/web/runtime/index.js:
Vue.prototype.__patch__ = inBrowser ? patch : noop
import * as nodeOps from 'web/runtime/node-ops'
export const patch: Function = createPatchFunction({ nodeOps, modules })
我们看一下nodeOps是啥?vue/src/platforms/web/runtime/node-ops.js:
/* @flow */
import { namespaceMap } from 'web/util/index'
export function createElement (tagName: string, vnode: VNode): Element {
const elm = document.createElement(tagName)
if (tagName !== 'select') {
return elm
}
// false or null will remove the attribute but undefined will not
if (vnode.data && vnode.data.attrs && vnode.data.attrs.multiple !== undefined) {
elm.setAttribute('multiple', 'multiple')
}
return elm
}
export function createElementNS (namespace: string, tagName: string): Element {
return document.createElementNS(namespaceMap[namespace], tagName)
}
export function createTextNode (text: string): Text {
return document.createTextNode(text)
}
export function createComment (text: string): Comment {
return document.createComment(text)
}
export function insertBefore (parentNode: Node, newNode: Node, referenceNode: Node) {
parentNode.insertBefore(newNode, referenceNode)
}
export function removeChild (node: Node, child: Node) {
node.removeChild(child)
}
export function appendChild (node: Node, child: Node) {
node.appendChild(child)
}
export function parentNode (node: Node): ?Node {
return node.parentNode
}
export function nextSibling (node: Node): ?Node {
return node.nextSibling
}
export function tagName (node: Element): string {
return node.tagName
}
export function setTextContent (node: Node, text: string) {
node.textContent = text
}
export function setStyleScope (node: Element, scopeId: string) {
node.setAttribute(scopeId, '')
}
翻过了一座又一座山,历经了千辛万苦,我们终于是看到了庐山真面目,小伙伴可别说你不熟悉这些代码哈~~
好啦~ 我们简单的回顾一下前面的内容哈,首先我们定义了一些响应式数据并监听变化—>执行mount方法–>生成render方法—>创建vnode对象–>通过update方法把vnode对象转换成dom元素…
好啦!! 整个vue的源码研究先到这了,之后会正对vue-loader以及watcher对象做一次解析.
先到这里啦,欢迎志同道合的小伙伴入群,一起交流一起学习~~ 加油骚年!!
qq群链接: