当前位置: 首页 > news >正文

网络创业培训平台/sem优化师是什么意思

网络创业培训平台,sem优化师是什么意思,东莞哪家做网站比较好,江西建设监理协会网站Vue源码解析-响应式原理课程目标源码目录结构准备工作-调试准备工作-Vue的不同构建版本完整版Vue举例小结寻找入口文件执行构建script/config.js文件执行过程从入口开始阅读源码记录Vue初始化过程小结-四个导出Vue的模块Vue初始化-静态成员src\core\index.js中,initGlobalAPI(V…

Vue源码解析-响应式原理

  • 课程目标
    • 源码目录结构
    • 准备工作-调试
    • 准备工作-Vue的不同构建版本
      • 完整版Vue举例
      • 小结
  • 寻找入口文件
    • 执行构建
    • script/config.js文件执行过程
  • 从入口开始
    • 阅读源码记录
  • Vue初始化过程
      • 小结-四个导出Vue的模块
    • Vue初始化-静态成员
      • src\core\index.js中,initGlobalAPI(Vue)详解
        • initUse(Vue) 注册Vue.use() 用来注册插件
        • initMixin(Vue) 注册Vue.mixin 实现混入
        • initExtend 注册Vue.extend 基于传入的options返回一个组件的构造函数
        • initAssetRegisters 注册Vue.directive(),Vue.component(),Vue.filter()
      • Vue初始化-实例成员
      • initMixin(Vue) 注册Vue.prototype._init()方法
        • 实例成员--initState
      • stateMixin(Vue)
      • eventsMixin(Vue)
      • lifecycleMixin(Vue) 混入了生命周期相关的方法
      • renderMixin(Vue)
    • 调试Vue初始化过程
    • 首次渲染过程

课程目标

  • Vue.js的静态成员和实例成员的初始化过程
  • 初次渲染过程
  • 数据响应式原理

源码目录结构

在这里插入图片描述

准备工作-调试

调试设置

  • 打包

    • 打包工具 Rollup
      • Vue.js 源码的打包工具使用的是 Rollup,比 webpack 轻量
      • webpack 会把所有文件当做模块,Rollup 只处理 js 文件,更适合在 Vue.js 这样的库中使用(开发项目使用 webpack,开发库使用 Rollup)
      • Rollup打包不会生成冗余的代码
  • 安装依赖

npm i 
  • 设置sourcemap
    • package.json 文件的 dev 脚本中添加参数 --sourcemap
"dev": "rollup -w -c scripts/config.js  --sourcemap --environment TARGET:web-full-dev"
  • 执行dev
    • npm run dev 执行打包,用的是rollup, -w 参数是监听文件的变化,文件变化自动重新打包; -c是执行的配置文件
  • 结果
    在这里插入图片描述
  • 以vue源码中examples中的grids为例进行调试
    在这里插入图片描述
  • 在进行代码调试的时候,如果没有开启sourcemap,则不会生成src目录(实际上是dist中的map文件指向src),断点不会进入src(源码),而直接进入打包后(dist)目录中压缩的vue.js文件,因为是压缩,编译后的代码,不方便调试,而我们希望断点直接进入src目录下的源码中调试,所以需要设置sourcemap

准备工作-Vue的不同构建版本

  • npm run build 重新打包所有文件
  • 官方文档-对不同构建版本的解释
  • 完整版:同时包含编译器和运行时的版本。
  • 编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码。=>将template装换成render函数
  • 运行时:用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码。基本上就是除去编译器的其它一切。

完整版Vue举例

<div id="app"></div><!-- 完整版 --><script src="../../dist/vue.js"></script><script>const vm = new Vue({el: "#app",template: "<h1>{{msg}}</h1>",data: {msg: "Hello Vue",},});</script>
  • 结果:可以正常显示
    在这里插入图片描述
<div id="app"></div><!-- 运行时版本 --><script src="../../dist/vue.runtime.js"></script><script>const vm = new Vue({el: "#app",template: "<h1>{{msg}}</h1>",data: {msg: "Hello Vue",},});</script>
  • 结果:报错
    在这里插入图片描述
  • 使用的是Vue的仅运行时版本,其中模板编译器不可用。方法一:将模板手动预编译为render函数,方法二:使用带编译器的vue版本
  • 方法二就是上面的引用完整版本的vue,下面介绍方法一
  • 方法一,将template模板字符串转换成render函数
<div id="app"></div><!-- 运行时版本 --><script src="../../dist/vue.runtime.js"></script><script>const vm = new Vue({el: "#app",render(h) {return h("h1", this.msg);},data: {msg: "Hello Vue",},});
  • 结果:能正常显示

小结

  • vue-cli默认引用的是运行时版本(不带编译器),并且是ESModule=>vue.runtime.esm.js
  • 因为vue-cli对webpack做了一个深度的封装,我们在vue-cli创建的项目中,看不到引入vue的版本,但vue-cli提供的一个命令行工具,通过这个工具可以查看webpack的配置
  • 命令行输入vue inspect
    在这里插入图片描述
  • webpack配置项在命令行中显示查看起来不太友好,直接输出到文件中(>代表把前面命令生成的结果输入到指定文件中)
vue inspect > output.js

在这里插入图片描述

  • 我们在开发项目的时候,会有很多单文件组件(.vue文件),这些单文件组件浏览器是不支持的,所以在打包的时候我们会将这些单文件组件转换成js对象,在转换js对象的过程中,还会将template模板转换成render函数(vue-loader来实现),所以单文件组件的运行是不需要编译器的

寻找入口文件

  • 查看dist/vue.js的构建过程

执行构建

npm run build
"build": "node scripts/build.js",
  • 通过node 生成所有版本的vue
npm run dev
"dev": "rollup -w -c scripts/config.js  --sourcemap --environment TARGET:web-full-dev"
  • rollup:通过rollup构建工具生成单一的vue版本
  • environment:设置环境变量TARGET:web-full-dev
    • web:生成web平台的vue版本
    • full:生成完整版的vue(含编译器+运行时)
    • dev:开发环境下的vue版本(不压缩)
  • script/config.js 的执行过程
    • 作用:生成rollup构建的配置文件
    • 使用环境变量TARGET:web-full-dev

script/config.js文件执行过程

  • 以npm run dev 为 例
"dev": "rollup -w -c scripts/config.js  --sourcemap --environment TARGET:web-full-dev",
const path = require('path')
const buble = require('rollup-plugin-buble')
const alias = require('rollup-plugin-alias')
const cjs = require('rollup-plugin-commonjs')
const replace = require('rollup-plugin-replace')
const node = require('rollup-plugin-node-resolve')
const flow = require('rollup-plugin-flow-no-whitespace')
const version = process.env.VERSION || require('../package.json').version
const weexVersion = process.env.WEEX_VERSION || require('../packages/weex-vue-framework/package.json').version
const featureFlags = require('./feature-flags')const banner ='/*!\n' +` * Vue.js v${version}\n` +` * (c) 2014-${new Date().getFullYear()} Evan You\n` +' * Released under the MIT License.\n' +' */'const weexFactoryPlugin = {intro () {return 'module.exports = function weexFactory (exports, document) {'},outro () {return '}'}
}const aliases = require('./alias')
const resolve = p => {const base = p.split('/')[0]if (aliases[base]) {return path.resolve(aliases[base], p.slice(base.length + 1))} else {return path.resolve(__dirname, '../', p)}
}const builds = {// Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify'web-runtime-cjs-dev': {entry: resolve('web/entry-runtime.js'),dest: resolve('dist/vue.runtime.common.dev.js'),format: 'cjs',env: 'development',banner},'web-runtime-cjs-prod': {entry: resolve('web/entry-runtime.js'),dest: resolve('dist/vue.runtime.common.prod.js'),format: 'cjs',env: 'production',banner},// Runtime+compiler CommonJS build (CommonJS)'web-full-cjs-dev': {// resolve函数将相对路径转换成绝对路径(web是别名)entry: resolve('web/entry-runtime-with-compiler.js'),dest: resolve('dist/vue.common.dev.js'),format: 'cjs',env: 'development',alias: { he: './entity-decoder' },banner},'web-full-cjs-prod': {entry: resolve('web/entry-runtime-with-compiler.js'),dest: resolve('dist/vue.common.prod.js'),format: 'cjs',env: 'production',alias: { he: './entity-decoder' },banner},// Runtime only ES modules build (for bundlers)'web-runtime-esm': {entry: resolve('web/entry-runtime.js'),dest: resolve('dist/vue.runtime.esm.js'),format: 'es',banner},// Runtime+compiler ES modules build (for bundlers)'web-full-esm': {entry: resolve('web/entry-runtime-with-compiler.js'),dest: resolve('dist/vue.esm.js'),format: 'es',alias: { he: './entity-decoder' },banner},// Runtime+compiler ES modules build (for direct import in browser)'web-full-esm-browser-dev': {entry: resolve('web/entry-runtime-with-compiler.js'),dest: resolve('dist/vue.esm.browser.js'),format: 'es',transpile: false,env: 'development',alias: { he: './entity-decoder' },banner},// Runtime+compiler ES modules build (for direct import in browser)'web-full-esm-browser-prod': {entry: resolve('web/entry-runtime-with-compiler.js'),dest: resolve('dist/vue.esm.browser.min.js'),format: 'es',transpile: false,env: 'production',alias: { he: './entity-decoder' },banner},// runtime-only build (Browser)'web-runtime-dev': {entry: resolve('web/entry-runtime.js'),dest: resolve('dist/vue.runtime.js'),format: 'umd',env: 'development',banner},// runtime-only production build (Browser)'web-runtime-prod': {entry: resolve('web/entry-runtime.js'),dest: resolve('dist/vue.runtime.min.js'),format: 'umd',env: 'production',banner},// Runtime+compiler development build (Browser)'web-full-dev': {entry: resolve('web/entry-runtime-with-compiler.js'), // 入口文件dest: resolve('dist/vue.js'), // 输出文件format: 'umd', // 模块化的方式env: 'development', // 模式alias: { he: './entity-decoder' }, // 别名banner // 文件头},// Runtime+compiler production build  (Browser)'web-full-prod': {entry: resolve('web/entry-runtime-with-compiler.js'),dest: resolve('dist/vue.min.js'),format: 'umd',env: 'production',alias: { he: './entity-decoder' },banner},// Web compiler (CommonJS).'web-compiler': {entry: resolve('web/entry-compiler.js'),dest: resolve('packages/vue-template-compiler/build.js'),format: 'cjs',external: Object.keys(require('../packages/vue-template-compiler/package.json').dependencies)},// Web compiler (UMD for in-browser use).'web-compiler-browser': {entry: resolve('web/entry-compiler.js'),dest: resolve('packages/vue-template-compiler/browser.js'),format: 'umd',env: 'development',moduleName: 'VueTemplateCompiler',plugins: [node(), cjs()]},// Web server renderer (CommonJS).'web-server-renderer-dev': {entry: resolve('web/entry-server-renderer.js'),dest: resolve('packages/vue-server-renderer/build.dev.js'),format: 'cjs',env: 'development',external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)},'web-server-renderer-prod': {entry: resolve('web/entry-server-renderer.js'),dest: resolve('packages/vue-server-renderer/build.prod.js'),format: 'cjs',env: 'production',external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)},'web-server-renderer-basic': {entry: resolve('web/entry-server-basic-renderer.js'),dest: resolve('packages/vue-server-renderer/basic.js'),format: 'umd',env: 'development',moduleName: 'renderVueComponentToString',plugins: [node(), cjs()]},'web-server-renderer-webpack-server-plugin': {entry: resolve('server/webpack-plugin/server.js'),dest: resolve('packages/vue-server-renderer/server-plugin.js'),format: 'cjs',external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)},'web-server-renderer-webpack-client-plugin': {entry: resolve('server/webpack-plugin/client.js'),dest: resolve('packages/vue-server-renderer/client-plugin.js'),format: 'cjs',external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)},// Weex runtime factory'weex-factory': {weex: true,entry: resolve('weex/entry-runtime-factory.js'),dest: resolve('packages/weex-vue-framework/factory.js'),format: 'cjs',plugins: [weexFactoryPlugin]},// Weex runtime framework (CommonJS).'weex-framework': {weex: true,entry: resolve('weex/entry-framework.js'),dest: resolve('packages/weex-vue-framework/index.js'),format: 'cjs'},// Weex compiler (CommonJS). Used by Weex's Webpack loader.'weex-compiler': {weex: true,entry: resolve('weex/entry-compiler.js'),dest: resolve('packages/weex-template-compiler/build.js'),format: 'cjs',external: Object.keys(require('../packages/weex-template-compiler/package.json').dependencies)}
}function genConfig (name) {// builds是一个对象,包含各种环境变量对应的配置项,name是环境变量的值/* builds:{// Runtime+compiler development build (Browser)'web-full-dev': {entry: resolve('web/entry-runtime-with-compiler.js'), // 入口文件dest: resolve('dist/vue.js'), // 输出文件format: 'umd', // 模块化的方式env: 'development', // 模式alias: { he: './entity-decoder' }, // 别名banner // 文件头}}*/// options是基础信息const opts = builds[name]// config是rollup的完整配置信息const config = {input: opts.entry, // 入口文件 src\platforms\web\entry-runtime-with-compiler.jsexternal: opts.external,plugins: [flow(),alias(Object.assign({}, aliases, opts.alias))].concat(opts.plugins || []),output: {file: opts.dest,format: opts.format,banner: opts.banner,name: opts.moduleName || 'Vue'},onwarn: (msg, warn) => {if (!/Circular/.test(msg)) {warn(msg)}}}// built-in varsconst vars = {__WEEX__: !!opts.weex,__WEEX_VERSION__: weexVersion,__VERSION__: version}// feature flagsObject.keys(featureFlags).forEach(key => {vars[`process.env.${key}`] = featureFlags[key]})// build-specific envif (opts.env) {vars['process.env.NODE_ENV'] = JSON.stringify(opts.env)}config.plugins.push(replace(vars))if (opts.transpile !== false) {config.plugins.push(buble())}Object.defineProperty(config, '_name', {enumerable: false,value: name})// 返回配置项return config
}
// 判断是否有TARGET环境变量
if (process.env.TARGET) {// 有环境变量TARGET:web-full-devmodule.exports = genConfig(process.env.TARGET)
} else {exports.getBuild = genConfigexports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
  • 入口文件是:src\platforms\web\entry-runtime-with-compiler.js

从入口开始

  • src\platforms\web\entry-runtime-with-compiler.js
  • 通过查看源码解决下面问题=>我们知道编译器会将template模板编译成render函数,当同时存在render函数和template模板的时候,源码中怎么运行?
    在这里插入图片描述
  • 此处的$mount是谁调用的呢?什么位置调用的呢?

阅读源码记录

  • el不能是body或者html标签
  • 如果没有render,把template转换成render函数
  • 如果有render函数,直接调用mount挂载dom
// 1:el不能是body或者html标签
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
if(!options.render){// 2:把template/el转换成render函数...
}
// 3:调用mount方法,挂载DOM
  • 调试代码
    <div id="app"></div><!-- 完整版本 --><script src="../../dist/vue.js"></script><script>const vm = new Vue({el: "#app",template: "<h3>Hello template</h3>",render(h) {return h("h3", "Hello render");},data: {msg: "Hello Vue",},});</script>

在这里插入图片描述

  • $mount <= Vue._init(Vue初始化) <= Vue (Vue构造函数) <= Vue实例化

Vue初始化过程

在这里插入图片描述
在这里插入图片描述

  • 完整版vue在入口文件src\platforms\web\entry-runtime-with-compiler.js中实际上相对于运行时vue版本针对Vue构造函数做了两件事

    • 增强Vue构造函数的原型对象$mount方法的功能,如果没有render函数,将模板(template)转换成render函数
    • Vue构造函数绑定了compile静态方法=>在$mount方法中将模板template字符串转换成render函数时调用
  • 在src\platforms\web\runtime\index.js中的Vu我们做了三件事件

    • 给Vue构造函数注册了平台相关的通用方法=>Vue.config.isReservedTag = isReservedTag
    • 注册了与平台相关的全局指令和组件(Vue全局的执行和组件分别保存在Vue.options.directives和Vue.optiosns.componnets中)
    • Vue构造函数的与原型对象上挂载_patch和$mount方法
      • _patch方法:将Vnode转换成对应的真实DOM
      • $mount 将差异渲染到页面上
  • src\core\index.js中给Vue构造函数添加静态属性和方法

  • 静态属性 Vue.config/Vue.options

  • 讲台方法 Vue.delete/Vue.set/Vue.nextTick/Vue.observable

小结-四个导出Vue的模块

  • src\platforms\web\entry-runtime-with-compiler.js

    • web平台相关入口
    • 重写了平台相关的$mount方法
    • 注册了Vue.compile()方法,传递一个HTML字符串,返回render函数
  • src\platforms\web\runtime\index.js

    • web平台相关
    • 注册和平台相关的全局指令:v-model,v-show
    • 注册和平台相关的全局组件:v-transition,v-transition-group
    • 全局方法
      • patch:把虚拟DOM转换成真实DOM
      • $mount:挂载方法
  • src\core\index.js

    • 与平台无关
    • 定义了Vue的静态属性和方法,initGlobalAPI(Vue)
  • src\core\instance\index.js

    • 与平台无关
    • 定义了构造函数,调用了this._init(options)方法
    • 定义了Vue实例的属性和方法

Vue初始化-静态成员

src\core\index.js中,initGlobalAPI(Vue)详解

/* @flow */import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import { observe } from 'core/observer/index'import {warn,extend,nextTick,mergeOptions,defineReactive
} from '../util/index'export function initGlobalAPI (Vue: GlobalAPI) {// config// Vue的Config静态属性const configDef = {}configDef.get = () => configif (process.env.NODE_ENV !== 'production') {configDef.set = () => {warn('Do not replace the Vue.config object, set individual fields instead.')}}Object.defineProperty(Vue, 'config', configDef)// exposed util methods.// NOTE: these are not considered part of the public API - avoid relying on// them unless you are aware of the risk.// 这些工具方法不视作全局API的一部分,除非你已经意识到某些风险,否则不要去依赖他们(只在Vue内部使用它们,我们可以忽略该代码)Vue.util = {warn,extend,mergeOptions,defineReactive}// 静态方法set/delete/nextTickVue.set = setVue.delete = delVue.nextTick = nextTick// observe:让一个对象可响应// 2.6 explicit observable APIVue.observable = <T>(obj: T): T => {observe(obj)return obj}// 初始化Vue.options对象,并扩展该对象(添加components,directives,filters等属性)Vue.options = Object.create(null)// ASSET_TYPES=['component','directive','filter'],// 分别存储全局的组件/指令/过滤器,即通过Vue.component(),Vue.directive(),Vue.filter()注册的全局组件,全局指令和全局过滤器,都会存储到对应的Vue.options属性上来ASSET_TYPES.forEach(type => {Vue.options[type + 's'] = Object.create(null)})// this is used to identify the "base" constructor to extend all plain-object// components with in Weex's multi-instance scenarios.Vue.options._base = Vue
/*
export function extend (to: Object, _from: ?Object): Object {for (const key in _from) {to[key] = _from[key]}return to
}
*///将一个对象的成员拷贝给另一个对象(浅拷贝)// 注册全局的keep-alive组件extend(Vue.options.components, builtInComponents)// 注册Vue.use()   用来注册插件initUse(Vue)// 注册Vue.mixin   实现混入initMixin(Vue)// 注册Vue.extend  基于传入的options返回一个组件的构造函数initExtend(Vue)// 注册Vue.directive(),Vue.component(),Vue.filter()// 因为三个方法的参数是一样的,所以一起注册的initAssetRegisters(Vue)
}
  • 注册全局的组件和指令,就是将组件和指令模块放到对应的Vue.options选项中
 // 注册全局的keep-alive组件extend(Vue.options.components, builtInComponents)

initUse(Vue) 注册Vue.use() 用来注册插件

/* @flow */import { toArray } from '../util/index'export function initUse (Vue: GlobalAPI) {Vue.use = function (plugin: Function | Object) {// installedPlugins:我们所安装的插件,this指向Vue构造函数const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))// 如果传入的插件已经存在,直接返回if (installedPlugins.indexOf(plugin) > -1) {return this}// additional parameters// 注册插件// 当Vue.use()传递多个参数的时候(第一个参数为插件)const args = toArray(arguments, 1)// 将Vue构造函数塞到args的第一个元素中,所以插件调用install方法时第一个参数就是Vue构造函数args.unshift(this)if (typeof plugin.install === 'function') {//如果plugin是对象,调用对象的install方法plugin.install.apply(plugin, args)} else if (typeof plugin === 'function') {plugin.apply(null, args)}// 将注册的组件插入到installedPlugins数组中,防止重复注册installedPlugins.push(plugin)return this}
}

initMixin(Vue) 注册Vue.mixin 实现混入

/* @flow */import { mergeOptions } from '../util/index'export function initMixin (Vue: GlobalAPI) {Vue.mixin = function (mixin: Object) {// 将用户传入的options和Vue构造函数自带的options合并(合并策略比较复杂,之后再看)this.options = mergeOptions(this.options, mixin)return this}
}

initExtend 注册Vue.extend 基于传入的options返回一个组件的构造函数

/* @flow */export function initExtend (Vue: GlobalAPI) {Vue.extend = function (extendOptions: Object): Function {const Super = thisconst Sub = function VueComponent (options) {// 子构造器有VueComponent静态方法this._init(options)}// 子构造器集成自Vue构造函数Sub.prototype = Object.create(Super.prototype)return Sub}
}

initAssetRegisters 注册Vue.directive(),Vue.component(),Vue.filter()

/* @flow */import { ASSET_TYPES } from 'shared/constants'
import { isPlainObject, validateComponentName } from '../util/index'export function initAssetRegisters (Vue: GlobalAPI) {/*** Create asset registration methods.*/ASSET_TYPES.forEach(type => {// ASSET_TYPES=['component','directive','filter'],Vue[type] = function (id: string,definition: Function | Object): Function | Object | void {if (!definition) {// 如果没有传递第二个参数,代表获取响应的组件,指令和过滤器return this.options[type + 's'][id]} else {if (type === 'component') {definition = this.options._base.extend(definition)}if (type === 'directive') {// 如果传入的是函数definition = { bind: definition, update: definition }}// 将全局注册的组件/指令/过滤器存储到Vue.options对应的属性中this.options[type + 's'][id] = definitionreturn definition}}})
}

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'
import { warn } from '../util/index'function Vue (options) {if (process.env.NODE_ENV !== 'production' &&!(this instanceof Vue)// Vue必须是构造函数,不能作为普通函数调用) {warn('Vue is a constructor and should be called with the `new` keyword')}this._init(options)
}
// 下面的方法是给Vue实例上混入成员
// 设置Vue实例的成员(定于Vue原型的对象的属性和方法)
// 注册vm的_init()方法,初始化vm
initMixin(Vue)
// 注册vm的$data/$props/$set/$delete/$watch
stateMixin(Vue)
// 初始化事件相关方法 $on/$emit/$once/$off
eventsMixin(Vue)
// _update/$forceUpdate/$destory
lifecycleMixin(Vue)
// _render/$nextTick
renderMixin(Vue)export default Vue
  • 定义了Vue的构造函数,在构造函数下面定义了几个以Minix结尾的方法,参数都是Vue的构造函数,目的是给Vue原型上混入相应的成员,也就是在Vue实例上增加了响应的成员

initMixin(Vue) 注册Vue.prototype._init()方法

  • 给Vue原型上添加了_init()方法,该方法在Vue构造函数中调用,相当于整个Vue的入口,所有的事情都是从它内部开始的
  • 定义了几个私有成员
    • vm = this;
    • vm._uid = uid++ vm的唯一标识
    • vm.isVue = true 如果当前的实例是Vue实例,不需要被observe处理(响应式)
    • vm._renderProxy = vm 设置渲染时的代理对象,当我们在渲染的时候会看到该对象的使用
    • vm._self = vm
  • 定义了实例的$options=>将Vue构造函数的options和用户new Vue时传入的options及vm自身的options进行合并
vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm)
  • vm实例属性和方法的初始化 (模块化)
    在这里插入图片描述
  • 其中initInjections(vm)和initProvide(vm)两个函数共同实现依赖注入

实例成员–initState

  • nitState帮我们初始化了vm.$options中的props,methods,data,computed和watch,并且将它们的成员都注入到vm实例中
export function initState (vm: Component) {vm._watchers = []const opts = vm.$options// 判断vm.$options中=是否有data,props,computed,watch,methods选项,如果有响应的选项,init(初始化)该选项if (opts.props) initProps(vm, opts.props)if (opts.methods) initMethods(vm, opts.methods)if (opts.data) {// 如果有data选项initData(vm)} else {// 没有data选项,observe把某个对象转换成响应式的对象observe(vm._data = {}, true /* asRootData */)}if (opts.computed) initComputed(vm, opts.computed)if (opts.watch && opts.watch !== nativeWatch) {initWatch(vm, opts.watch)}
}
  • initProps(vm, opts.props) 将vm._props中的成员转换成响应式,并且注入到vm实例中
  • initMethods(vm, opts.methods) 把选项中的methods成员注入到vm实例,在注入之前,先判断命名是否在props中有重名的属性,并且检测了下函数命名的规范,不能以_或者$开头
  • initData(vm) 把data中的成员转换成响应式,并且注入到vm实例中

stateMixin(Vue)

  • Vue原型上添加data/data/data/props属性
 const dataDef = {}dataDef.get = function () { return this._data }const propsDef = {}propsDef.get = function () { return this._props }if (process.env.NODE_ENV !== 'production') {// 在开发环境下,不允许给$data/$props赋值dataDef.set = function () {warn('Avoid replacing instance root $data. ' +'Use nested data properties instead.',this)}propsDef.set = function () {warn(`$props is readonly.`, this)}}// 访问$data和$props就像访问_data与_props一样,可访问,不能重新赋值Object.defineProperty(Vue.prototype, '$data', dataDef)Object.defineProperty(Vue.prototype, '$props', propsDef)
  • Vue原型上添加$set/$delete/$watch方法
  // 给原型上挂载$watcher方法,监控数据变化Vue.prototype.$watch = function (expOrFn: string | Function,cb: any,options?: Object): Function {const vm: Component = thisif (isPlainObject(cb)) {return createWatcher(vm, expOrFn, cb, options)}options = options || {}options.user = trueconst watcher = new Watcher(vm, expOrFn, cb, options)if (options.immediate) {try {cb.call(vm, watcher.value)} catch (error) {handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)}}return function unwatchFn () {watcher.teardown()}}

eventsMixin(Vue)

  • 在原型上挂载$on/$emit/$off/$once方法
 Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {const vm: Component = this// 当第一个参数是数组的时候,遍历调用$on,可以给多个事件注册同一个事件处理函数if (Array.isArray(event)) {for (let i = 0, l = event.length; i < l; i++) {vm.$on(event[i], fn)}} else {// 判断该事件是否在_events数组中存在,没有的话就赋值空数组,然后将事件处理函数添加到该数组中(vm._events[event] || (vm._events[event] = [])).push(fn)// optimize hook:event cost by using a boolean flag marked at registration// instead of a hash lookupif (hookRE.test(event)) {vm._hasHookEvent = true}}return vm}

lifecycleMixin(Vue) 混入了生命周期相关的方法

  • 在原型上挂载 _update/forceUpdate/forceUpdate/forceUpdate/destory方法
    在这里插入图片描述
  • update方法中最重要的是_patch 方法;将虚拟dom转换成真实dom挂载到vm.$el上
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {const vm: Component = thisconst prevEl = vm.$elconst prevVnode = vm._vnodeconst restoreActiveInstance = setActiveInstance(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 */)} else {// updates// 数据变化vm.$el = vm.__patch__(prevVnode, vnode)}restoreActiveInstance()// update __vue__ referenceif (prevEl) {prevEl.__vue__ = null}if (vm.$el) {vm.$el.__vue__ = vm}// if parent is an HOC, update its $el as wellif (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.}

renderMixin(Vue)

  • _render/$nextTick
    在这里插入图片描述

调试Vue初始化过程

  • 在引入Vue的四个文件中依次打上断点
  • 与平台相关(src\platforms\web\entry-runtime-with-compiler.js)
    在这里插入图片描述
  • 与平台相关(src\platforms\web\runtime\index.js)
    在这里插入图片描述
  • 与平台无关(src\core\index.js)
    在这里插入图片描述
  • 与平台无关(src\core\instance\index.js)
    在这里插入图片描述
  • 因为我们始终想观察Vue的变化,监听Vue
    在这里插入图片描述
  • F5刷新,定位到initMixin在这里插入图片描述
  • F10执行initMixin(Vue)函数,发现在Vue.pototype上混入了_init方法
    在这里插入图片描述
  • F10执行stateMixin(Vue),发现在Vue.pototype上混入了$data,$props,$set,$delete,$watch成员
    在这里插入图片描述
  • F10执行evenetMixin(Vue),发现在Vue.pototype上混入了$on,$emit,$off,$once成员
    在这里插入图片描述
  • F10执行lifecycleMixin(Vue),发现在Vue.pototype上混入了_update,$forceUpdate,$destory成员
    在这里插入图片描述
  • F10执行renderMixin(Vue),发现在Vue.pototype上混入了_render,$nextTick成员
    在这里插入图片描述
  • F8跳到下一断点initGlobalAPI(Vue)=>给Vue构造函数挂载静态成员(属性和方法)
    • 属性
      • Vue.config属性

      • Vue.options属性[components",“directives”,“filters”,"_base"],其中components中含有keep-alive全局组件,_base指向Vue构造函数本身

      • Vue.set

      • Vue.delete

      • Vue.nextTick

      • Vue.observable
        在这里插入图片描述

      • 方法

        • Vue.use()
        • Vue.mixin()
        • Vue.extend()
        • Vue.component()
        • Vue.directive()
        • Vue.filter()
          在这里插入图片描述
  • 当我们调用Vue.component(),Vue.directive(),Vue.fileter()注册全局的组件,指令和过滤器的时候,会将该模块注入带Vue.options相应的属性中

首次渲染过程

  • 在引入Vue四个文件夹定义了Vue属性和方法,以及Vue原型对象的属性和方法,在项目中const Vue from "vue"完成了上述功能
  • new Vue创建Vue实例(vm)的时候调用了this._init()方法
  • 打断点
    在这里插入图片描述
  • 重点关注_init()函数中的 vm.mount(vm.mount(vm.mount(vm.options.el),当是编译版本的话
    在这里插入图片描述
  • 在src\platforms\web\runtime\index.js中
    在这里插入图片描述
  • 关注mountComponent做了什么
import { mountComponent } from 'core/instance/lifecycle'

参数一:vm实例 参数二:el

export function mountComponent (vm: Component,el: ?Element,hydrating?: boolean
): Component {vm.$el = el//判断当前选项是否有render函数if (!vm.$options.render) {vm.$options.render = createEmptyVNodeif (process.env.NODE_ENV !== 'production') {/* istanbul ignore if */// 如果是运行时版本,且传入的tempalte选项(没有render选项),则抛出一个警告// 当前使用的是运行时版本,编译器是无效的,你应该传入render函数或者使用带编译器的vue版本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)}}}// 触发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(Vue)生成的Vue.prototype._update()方法及renderMixin(Vue)生成的Vue.prototype._render方法// vm._render()内部调用的options选项中的render函数生成虚拟dom=>vnode = render.call(vm._renderProxy, vm.$createElement)// vm._update会将vnode转换成真实dom挂载到vm.$el属性上  vm.$el = vm.__patch__(prevVnode, vnode),视图此时还没更新哦vm._update(vm._render(), hydrating)}// 此时的updateComponent只是定义了,还没执行(执行的结果是真实dom对象)}// 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// 创建Watcher对象,传入updateComponent函数,所以updateComponent是在Watcher中调用的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 = true// 页面挂载完毕callHook(vm, 'mounted')}return vm
}
  • 再来重点关注Watcher中调用的updateComponent方法=>observer中的代码都是和响应式相关的
src\core\observer\watcher.js

在Vue中Watcer有三种
第一种:渲染watcher,也就是当前我们创建的Watcher,
第二种:计算属性的watcher
第三种:侦听器的watcher,也就是说计算属性和侦听器都是通过watcher来实现的
Watcher
参数一:vm实例
参数二:expOrFn(expression或者function))可以是函数也可以是字符串
参数三:回调函数cb
参数四:选项options
参数五:isRenderWatcher是否是渲染watcher
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

http://www.jmfq.cn/news/5185459.html

相关文章:

  • 如何找外贸网站建设公司/找客户的软件有哪些
  • 受欢迎的锦州网站建设/玉溪seo
  • 网站基本模板/yw77731域名查询
  • 深圳网站开发/企业营销管理
  • 优秀企业网站设计/google chrome
  • 柳州做网站优化/营销网站案例
  • 自己做网站需要下载什么软件/软件商店安装
  • 乌兰察布做网站/公司网站建设全包
  • php网站留言/广州网站优化服务商
  • 网站策划建站/百度天眼查
  • 做网站哪家好 青岛/武汉做seo
  • java做视频网站/百度一下首页版
  • 网站建设保教/中国十大互联网公司排名
  • ip地址做网站/网站内容优化怎么去优化呢
  • 自己做资金盘网站/营销型网站建设目标
  • 北京网站建设1000zhu/搜索数据
  • 114百事通做网站600/律师推广网站排名
  • 苏州网站建设技术/刷百度指数
  • 科技公司起名字大全免费/肇庆seo排名外包
  • 备案注销网站还有吗/网站建设需要啥
  • 互联网行业网站建设/福建seo顾问
  • 前端做项目网站/百度在线
  • 公司网站的开发/小说排行榜
  • 沈阳模板网站制作/营销型网页设计
  • 网站上线前营销推广工作准备/seo有哪些经典的案例
  • 济宁建设企业网站/域名免费查询
  • 项目营销策划公司/seo站外推广
  • 网站宣传搭建/茶叶网络营销策划方案
  • 北京展览馆网站建设/明天上海封控16个区
  • 网站建设制作视频教程/百度最新秒收录方法2022