手机主题制作软件,厦门百度seo点击软件,购物网页代码,上海怎样建设网站一、思考
我们都听过知其然知其所以然这句话
那么不知道大家是否思考过new Vue()这个过程中究竟做了些什么#xff1f;
过程中是如何完成数据的绑定#xff0c;又是如何将数据渲染到视图的等等
二、分析
首先找到vue的构造函数
源码位置#xff1a;src\core\instance\…一、思考
我们都听过知其然知其所以然这句话
那么不知道大家是否思考过new Vue()这个过程中究竟做了些什么
过程中是如何完成数据的绑定又是如何将数据渲染到视图的等等
二、分析
首先找到vue的构造函数
源码位置src\core\instance\index.js
function Vue (options) {if (process.env.NODE_ENV ! production !(this instanceof Vue)) {warn(Vue is a constructor and should be called with the new keyword)}this._init(options)
}options是用户传递过来的配置项如data、methods等常用的方法
vue构建函数调用_init方法但我们发现本文件中并没有此方法但仔细可以看到文件下方定定义了很多初始化方法
initMixin(Vue); // 定义 _init
stateMixin(Vue); // 定义 $set $get $delete $watch 等
eventsMixin(Vue); // 定义事件 $on $once $off $emit
lifecycleMixin(Vue);// 定义 _update $forceUpdate $destroy
renderMixin(Vue); // 定义 _render 返回虚拟dom首先可以看initMixin方法发现该方法在Vue原型上定义了_init方法
源码位置src\core\instance\init.js
Vue.prototype._init function (options?: Object) {const vm: Component this// a uidvm._uid uidlet 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 observedvm._isVue true// merge options// 合并属性判断初始化的是否是组件这里合并主要是 mixins 或 extends 的方法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 { // 合并vue属性vm.$options mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm)}/* istanbul ignore else */if (process.env.NODE_ENV ! production) {// 初始化proxy拦截器initProxy(vm)} else {vm._renderProxy vm}// expose real selfvm._self vm// 初始化组件生命周期标志位initLifecycle(vm)// 初始化组件事件侦听initEvents(vm)// 初始化渲染方法initRender(vm)callHook(vm, beforeCreate)// 初始化依赖注入内容在初始化data、props之前initInjections(vm) // resolve injections before data/props// 初始化props/data/method/watch/methodsinitState(vm)initProvide(vm) // resolve provide after data/propscallHook(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)}}仔细阅读上面的代码我们得到以下结论
在调用beforeCreate之前数据初始化并未完成像data、props这些属性无法访问到
到了created的时候数据已经初始化完成能够访问data、props这些属性但这时候并未完成dom的挂载因此无法访问到dom元素
挂载方法是调用vm.$mount方法
initState方法是完成props/data/method/watch/methods的初始化
源码位置src\core\instance\state.js
export function initState (vm: Component) {// 初始化组件的watcher列表vm._watchers []const opts vm.$options// 初始化propsif (opts.props) initProps(vm, opts.props)// 初始化methods方法if (opts.methods) initMethods(vm, opts.methods)if (opts.data) {// 初始化data initData(vm)} else {observe(vm._data {}, true /* asRootData */)}if (opts.computed) initComputed(vm, opts.computed)if (opts.watch opts.watch ! nativeWatch) {initWatch(vm, opts.watch)}
}我们和这里主要看初始化data的方法为initData它与initState在同一文件上
function initData (vm: Component) {let data vm.$options.data// 获取到组件上的datadata vm._data typeof data function? getData(data, vm): data || {}if (!isPlainObject(data)) {data {}process.env.NODE_ENV ! production warn(data functions should return an object:\n https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function,vm)}// proxy data on instanceconst keys Object.keys(data)const props vm.$options.propsconst methods vm.$options.methodslet i keys.lengthwhile (i--) {const key keys[i]if (process.env.NODE_ENV ! production) {// 属性名不能与方法名重复if (methods hasOwn(methods, key)) {warn(Method ${key} has already been defined as a data property.,vm)}}// 属性名不能与state名称重复if (props hasOwn(props, key)) {process.env.NODE_ENV ! production warn(The data property ${key} is already declared as a prop. Use prop default value instead.,vm)} else if (!isReserved(key)) { // 验证key值的合法性// 将_data中的数据挂载到组件vm上,这样就可以通过this.xxx访问到组件上的数据proxy(vm, _data, key)}}// observe data// 响应式监听data是数据的变化observe(data, true /* asRootData */)
}仔细阅读上面的代码我们可以得到以下结论
初始化顺序props、methods、data
data定义的时候可选择函数形式或者对象形式组件只能为函数形式
关于数据响应式在这就不展开详细说明
上文提到挂载方法是调用vm.$mount方法
源码位置
Vue.prototype.$mount function (el?: string | Element,hydrating?: boolean
): Component {// 获取或查询元素el el query(el)/* istanbul ignore if */// vue 不允许直接挂载到body或页面文档上if (el document.body || el document.documentElement) {process.env.NODE_ENV ! production warn(Do not mount Vue to html or body - mount to normal elements instead.)return this}const options this.$options// resolve template/el and convert to render functionif (!options.render) {let template options.template// 存在template模板解析vue模板文件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 getOuterHTML(el)}if (template) {/* istanbul ignore if */if (process.env.NODE_ENV ! production config.performance mark) {mark(compile)}/*** 1.将temmplate解析ast tree* 2.将ast tree转换成render语法字符串* 3.生成render方法*/const { render, staticRenderFns } compileToFunctions(template, {outputSourceRange: process.env.NODE_ENV ! production,shouldDecodeNewlines,shouldDecodeNewlinesForHref,delimiters: options.delimiters,comments: options.comments}, this)options.render renderoptions.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)
}阅读上面代码我们能得到以下结论
不要将根元素放到body或者html上
可以在对象中定义template/render或者直接使用template、el表示元素选择器
最终都会解析成render函数调用compileToFunctions会将template解析成render函数
对template的解析步骤大致分为以下几步
将html文档片段解析成ast描述符
将ast描述符解析成字符串
生成render函数
生成render函数挂载到vm上后会再次调用mount方法
源码位置src\platforms\web\runtime\index.js
// public mount method
Vue.prototype.$mount function (el?: string | Element,hydrating?: boolean
): Component {el el inBrowser ? query(el) : undefined// 渲染组件return mountComponent(this, el, hydrating)
}调用mountComponent渲染组件
export function mountComponent (vm: Component,el: ?Element,hydrating?: boolean
): Component {vm.$el el// 如果没有获取解析的render函数则会抛出警告// render是解析模板文件生成的if (!vm.$options.render) {vm.$options.render createEmptyVNodeif (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 {// 没有获取到vue的模板文件warn(Failed to mount component: template or render function not defined.,vm)}}}// 执行beforeMount钩子callHook(vm, beforeMount)let updateComponent/* istanbul ignore if */if (process.env.NODE_ENV ! production config.performance mark) {updateComponent () {const name vm._nameconst id vm._uidconst 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 () {// 实际调⽤是在lifeCycleMixin中定义的_update和renderMixin中定义的_rendervm._update(vm._render(), hydrating)}}// we set this to vm._watcher inside the watchers constructor// since the watchers initial patch may call $forceUpdate (e.g. inside child// components mounted hook), which relies on vm._watcher being already defined// 监听当前组件状态当有数据变化时更新组件new Watcher(vm, updateComponent, noop, {before () {if (vm._isMounted !vm._isDestroyed) {// 数据更新引发的组件更新callHook(vm, beforeUpdate)}}}, true /* isRenderWatcher */)hydrating false// manually mounted instance, call mounted on self// mounted is called for render-created child components in its inserted hookif (vm.$vnode null) {vm._isMounted truecallHook(vm, mounted)}return vm
}三、结论
new Vue的时候调用会调用_init方法
定义 s e t 、 set、 set、get 、 d e l e t e 、 delete、 delete、watch 等方法 定义 o n 、 on、 on、off、 e m i t 、 emit、 emit、off等事件 定义 _update、 f o r c e U p d a t e 、 forceUpdate、 forceUpdate、destroy生命周期 调用$mount进行页面的挂载
挂载的时候主要是通过mountComponent方法
定义updateComponent更新函数
执行render生成虚拟DOM
_update将虚拟DOM生成真实DOM结构并且渲染到页面中