实例讲解vue源码架构


Posted in Javascript onJanuary 24, 2019

下载

去github上下载Vue https://github.com/vuejs/vue

npm install 
npm run dev

运行起来

rollup + flow

vue使用使用rollup打包,flow规范数据类型

rollup可以先用webpack套用,读起来差不多,时间有限,毕竟只有5分钟,这个就不用去看rollup文档了

入口

打开package.json

我们看scripts配置

"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
 "dev:cjs": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-cjs-dev",

找到scripts/config.js

打开

根据配置TARGET的不同会选择不同的config

同时在这里配置了process.env.NODE_ENV 环境

TARGET有CommonJS,ES Modules,UMD关于js引入类型的

还有weex,ssr

'web-runtime-cjs-dev': {
  entry: resolve('web/entry-runtime.js'),
  dest: resolve('dist/vue.runtime.common.dev.js'),
  format: 'cjs',
  env: 'development',
  banner
 }

在alias.js下设置了别名路径

我们先介绍src/platforms

里面有web和weex 分别的web和weex入口

在web文件下是CommonJS,ES Modules,UMD关于js引入类型,server的打包入口

打开web/entry-runtime.js

引入

import Vue from './runtime/index'
export default Vue

打开./runtime/index

import Vue from 'core/index'
 
Vue.prototype.$mount = function (
 el?: string | Element,
 hydrating?: boolean
): Component {
 el = el && inBrowser ? query(el) : undefined
 return mountComponent(this, el, hydrating)
}
export default Vue

在vue原型上添加了mount方法

处理了devtools,没有安装提醒安装devtools

给了这句提示dev环境提示

You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html

platforms目录夹讲解完毕

core目录

打开core/instance/index

映入眼前的是

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)
}
 
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
 
export default Vue

先执行的是initMixin(Vue)

打开init

export function initMixin (Vue) {
 Vue.prototype._init = function (options?: Object) {
  const vm = 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
  // 处理传入的options
  // 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 {
    // 传入的options,默认的options一起合并挂载到vm.$options上
   vm.$options = mergeOptions(
    resolveConstructorOptions(vm.constructor),
    options || {},
    vm
   )
  }
  /* istanbul ignore else */
  if (process.env.NODE_ENV !== 'production') {
   // 代理
   initProxy(vm)
  } else {
   vm._renderProxy = vm
  }
  // 生命周期
  initLifecycle(vm)
   // emit on 事件
  initEvents(vm)
  // 处理render vdom
  initRender(vm)
  callHook(vm, 'beforeCreate')
  // 处理Injections
  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)
  }
  // 渲染到dom
  if (vm.$options.el) {
   vm.$mount(vm.$options.el)
  }
 }
}

lifecycle

打开 lifecycle

export function callHook (vm: Component, hook: string) {
 // disable dep collection when invoking lifecycle hooks
 pushTarget()
 //执行对象的周期函数,周期函数最后被处理成数组
 const handlers = vm.$options[hook]
 const info = `${hook} hook`
 if (handlers) {
  for (let i = 0, j = handlers.length; i < j; i++) {
   invokeWithErrorHandling(handlers[i], vm, null, vm, info)
  }
 }
 if (vm._hasHookEvent) {
  vm.$emit('hook:' + hook)
 }
 popTarget()

callHook 的时候,是执行相应周期,开发者在周期函数里所写的

Events

initEvents实现了 emit on 等方法,请参考监听者订阅者模式,这里不详解

render
renderMixin函数
添加了 $nextTick _render 原型对象

$nextTick会在dom跟新后立即调用

nextTick(fn, this)是一个自执行函数

_render返回的是node的js数据,还不是dom

做了Vdom

initRender函数
给vm添加了_c和 $createElement用来渲染的方法

state

if (!(key in vm)) {
   proxy(vm, `_props`, key)
  }

给vue属性做代理,访问this.a可以得到this.data.a 的值

export function initState (vm: Component) {
 vm._watchers = []
 const opts = vm.$options
 if (opts.props) initProps(vm, opts.props)
 if (opts.methods) initMethods(vm, opts.methods)
 if (opts.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)
 }
}

给数据做监听

stateMixin函数

添加原型对象

Vue.prototype.$set = set
Vue.prototype.$delete = del

其他

src/compiler 做了编译处理

core/componetd 做了keep-alive

core/util 封装了通用方法

core/vdom vdom算法

以上整体架构分析完毕

实例讲解vue源码架构

Javascript 相关文章推荐
几个比较经典常用的jQuery小技巧
Mar 01 Javascript
Jquery 跨域访问 Lightswitch OData Service的方法
Sep 11 Javascript
JavaScript函数模式详解
Nov 07 Javascript
js实现鼠标触发图片抖动效果的方法
Feb 27 Javascript
jQuery获取cookie值及删除cookie用法实例
Apr 15 Javascript
Google Maps基础及实例解析
Aug 06 Javascript
js, jQuery实现全选、反选功能
Mar 08 Javascript
javascript基本常用排序算法解析
Sep 27 Javascript
vue中组件通信的八种方式(值得收藏!)
Aug 09 Javascript
浅谈laytpl 模板空值显示null的解决方法及简单的js表达式
Sep 19 Javascript
VUE渲染后端返回含有script标签的html字符串示例
Oct 28 Javascript
vue绑定class的三种方法
Dec 24 Vue.js
详解Node.js amqplib 连接 Rabbit MQ最佳实践
Jan 24 #Javascript
JavaScript私有变量实例详解
Jan 24 #Javascript
小程序从手动埋点到自动埋点的实现方法
Jan 24 #Javascript
JavaScript递归函数定义与用法实例分析
Jan 24 #Javascript
jQuery实现当拉动滚动条到底部加载数据的方法分析
Jan 24 #jQuery
vue结合element-ui使用示例
Jan 24 #Javascript
VUE+Element环境搭建与安装的方法步骤
Jan 24 #Javascript
You might like
收音机另类DIY - 纸巾盒做外壳
2021/03/02 无线电
php 如何获取数组第一个值
2013/08/06 PHP
解决file_get_contents无法请求https连接的方法
2013/12/17 PHP
php实现mysql数据库连接操作及用户管理
2015/11/08 PHP
ThinkPHP表单令牌错误的相关解决方法分析
2016/05/20 PHP
PHP socket 模拟POST 请求实例代码
2016/07/18 PHP
thinkPHP框架实现的简单计算器示例
2018/12/07 PHP
jQuery操作元素css样式的三种方法
2014/06/04 Javascript
jQuery学习笔记之创建DOM元素
2015/01/19 Javascript
javascript实现字符串反转的方法
2015/02/05 Javascript
jquery实现无限分级横向导航菜单的方法
2015/03/12 Javascript
JQuery中模拟image的ajaxPrefilter与ajaxTransport处理
2015/06/19 Javascript
JavaScript Array对象详解
2016/03/01 Javascript
Node.js实现兼容IE789的文件上传进度条
2016/09/02 Javascript
JS简单获取当前日期时间的方法(如:2017-03-29 11:41:10 星期四)
2017/03/29 Javascript
原JS实现banner图的常用功能
2017/06/12 Javascript
微信小程序项目实践之主页tab选项实现
2018/07/18 Javascript
jQuery使用bind动态绑定事件无效的处理方法
2018/12/11 jQuery
基于vue-cli 路由 实现类似tab切换效果(vue 2.0)
2019/05/08 Javascript
vue 使用外部JS与调用原生API操作示例
2019/12/02 Javascript
Vue如何基于es6导入外部js文件
2020/05/15 Javascript
Python单例模式实例分析
2015/01/14 Python
python 中的list和array的不同之处及转换问题
2018/03/13 Python
深入浅析Python获取对象信息的函数type()、isinstance()、dir()
2018/09/17 Python
python实现简单加密解密机制
2019/03/19 Python
python 已知一个字符,在一个list中找出近似值或相似值实现模糊匹配
2020/02/29 Python
Python实现给PDF添加水印的方法
2021/01/25 Python
html5 figure和figcaption的使用方法
2018/09/10 HTML / CSS
奢华时尚的独特视角:La Garçonne
2018/06/07 全球购物
英国家庭和商业健身器材购物网站:Fitness Options
2018/07/05 全球购物
语文教育专业推荐信范文
2013/11/25 职场文书
教师求职自荐书
2014/06/14 职场文书
秋季运动会演讲稿
2014/09/16 职场文书
在前女友婚礼上,用Python破解了现场的WIFI还把名称改成了
2021/05/28 Python
总结Java对象被序列化的两种方法
2021/06/30 Java/Android
Python调用腾讯API实现人脸身份证比对功能
2022/04/04 Python