【js】vue 2.5.1 源码学习 (七) 初始化之 initState 响应式系统基本思路
// 大体思路(六)
// 本节内容:
// 生命周期的钩子函数的实现
// ==》 callHook(vm , 'befroeCreate')
// beforeCreate 实例创建之后 事件数据还未创建
// ==>initState(vm) // 初始化数据
// ==> initProps(vm,opts.props) 待续
// ==> initMethods 待续
// ==> initComputed 待续
// ==> if data initData else { observer(vm._data={},true)}
// initData
// ==> getData(data,vm) 如果是一个函数直接调用这个函数
// ==> data methods props 里面属性名称不能相同
// ==> proxy(vm,data,key) 双向数据绑定的地方 defineProperty(vm,key,sharedProperty)
// ==> sharedProperty // 公共访问器对象
// 响应式系统 (function(global,factory){
// 兼容 cmd
typeof exports === 'object' && module !== 'undefined' ? module.exports = factory():
// Amd
typeof define === 'function' && define.amd ? define(factory) : global.Vue = factory();
})(this,function(){
var uip = 0;
function warn(string){
console.error('Vue Wran:' + string)
}
function warnNonpresent(target,key){
warn('属性方法'+ key + '未在实例对象上定义,渲染功能正在尝试访问这个不存在的属性!')
}
function resolveConstructorOptions(Con){
var options = Con.options;
// 判断是否为vm的实例 或者是子类
return options
}
var hasOwnPropeerty = Object.prototype.hasOwnProperty
function hasOwn(obj , key){
return hasOwnPropeerty.call(obj,key)
}
function makeMap(str, expectsLoweraseC){
if(expectsLoweraseC){
str = str.toLowerCase()
}
var map = Object.create(null)
var list = str.split(',')
for(var i = 0 ; i < list.length; i++){
map[list[i]] = true
}
return function(key){
return map[key] }
}
var isbuiltInTag = makeMap('slot,component',true)
var isHTMLTag = makeMap(
'html,body,base,head,link,meta,style,title,' +
'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +
'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +
'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +
's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +
'embed,object,param,source,canvas,script,noscript,del,ins,' +
'caption,col,colgroup,table,thead,tbody,td,th,tr,' +
'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +
'output,progress,select,textarea,' +
'details,dialog,menu,menuitem,summary,' +
'content,element,shadow,template,blockquote,iframe,tfoot'
);
var isSVG = makeMap(
'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' +
'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' +
'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',
true
);
var ASSET_TYPES = [
'component',
'directive',
'filter'
]; var LIFECYCLE_HOOKS = [
'beforeCreate',
'created',
'beforeMount',
'mounted',
'beforeUpdate',
'updated',
'beforeDestroy',
'destroyed',
'activated',
'deactivated',
'errorCaptured'
];
var noop = function (){}
var isReservedTag = function(key){
return isHTMLTag(key) || isSVG(key)
}
// 检测data的属性名称是否为 _ 或者$ 开始的,这些的话是vue的其他属性的值。
function isReserved(key) {
var c = key.charCodeAt(0)
return c === 0x24 || c === 0x5F
}
function validataComponentName(key){
//检测component 的自定义名称是否合格
// 只能是字母开头或下划线,必须是字母开头
if(!(/^[a-zA-Z][\w-]*$/g.test(key))){
warn('组件的名称必须是字母或中横线,必须由字母开头')
}
// 1. 不能为内置对象,2.不能是html ,和avg的内部标签
if( isbuiltInTag(key) || isReservedTag(key)){
warn('不能为html标签或者avg的内部标签')
}
}
function checkComonpents(child){
for(var key in child.components){
validataComponentName(key)
}
}
// 配置对象
var config = {
// 自定义的策略
optionMergeStrategies:{}
}
var strats = config.optionMergeStrategies
strats.el = function(parent,child , key , vm){ if(!vm){
warn('选项'+key+'只能在vue实例用使用')
}
return defaultStrat(parent,child , key , vm)
}
function mergeData(to,form){
// 终极合并
if(!form){
return to
}
// 具体合并。
}
function mergeDataorFn(parentVal,childVal,vm){
// 合并 parentVal childVal 都是函数
if(!vm){
if(!childVal){
return parentVal
}
if(!parentVal){
return childVal
}
return function mergeDataFn(parentVal,childVal,vm){//只是一个函数 什么样的情况下调用 加入响应式系统
// 合并子组件对应的data 和 父组件对应的data
return mergeData(
typeof parentVal === 'function' ? parentVal.call(this,this) : parentVal, // -----忘记写
typeof childVal === 'function' ? childVal.call(this,this): childVal) // -----忘记写
}
}else{ // vue实例
return function mergeInstanceDataFn(){//只是一个函数 什么样的情况下调用 加入响应式系统
var InstanceData = typeof childVal === 'function' ? childVal.call(vm,vm): childVal; // -----忘记写
var defaultData = typeof parentVal === 'function' ? parentVal.call(vm,vm): parentVal; // -----忘记写
if(InstanceData){
return mergeData(InstanceData,defaultData)
}else{ // -----忘记写
return defaultData
} }
}
}
strats.data = function(parent,child , key , vm){
if(!vm){
// console.log(typeof child === 'function')
if(child && !(typeof child === 'function')){
warn('data必须返回是一个function')
}
return mergeDataorFn(parent,child)
}
return mergeDataorFn(parent,child,vm)
}
// 生命周期策略的合并,值等于一个function 如果是有两个,放到一个数组里面。
function mergeHook(parentVal,childVal,key,vm){
// console.log(key)
// console.log(parentVal.concat(childVal) )
return childVal ? parentVal ? parentVal.concat(childVal):
Array.isArray(childVal) ? childVal : [childVal] : parentVal
}
LIFECYCLE_HOOKS.forEach(function(key){
strats[key] = mergeHook
});
// 检测是否为object
function isPlainObject(obj){
return Object.prototype.toString.call(obj) === '[object Object]'
}
function assetObjectType(obj){
if(!isPlainObject(obj)){
warn('选项的值'+obj+'无效:必须是一个对象的')
}
}
// 对parent实现链式调用。
function extend(to,form){
for(key in form){ to[key] = form[key]
}
return to
}
// 实现Assets 的策略合并 conmponents filter diretive
function mergeAssets(parentVal,childVal,key,vm){
var parent = Object.create(parentVal || null) // 保证子类的每个值的指向都是一个新的object。否则回出现相互引用的现象。
if(childVal){
assetObjectType(childVal)
return extend(parent,childVal)
}
return parent
}
ASSET_TYPES.forEach(function(key){
strats[key+'s'] = mergeAssets
})
// 实现watch的策略和并,将相同的属性放到一个数组里面。
strats.watch = function(parentVal,childVal , key , vm){
if(!childVal){
return Object.create(parentVal)
}
var res = {}
res = extend(res,parentVal)
for(var key in childVal){
var parent = res[key]
var child = childVal[key]
res[key] = parent ? Array.isArray(parent) ? parent.concat(child) : [parent].concat(child) :
Array.isArray(child) ? child : [child] ;
}
return res
}
// 实现props指令的合并策略
strats.props = function(parentVal,childVal , key , vm){
if(!childVal){
return parentVal
}
var res = Object.create( null)
extend(res,parentVal)
if(childVal){
extend(res,childVal)
}
return res
}
function defaultStrat(parent,child , key , vm){
return child === undefined ? parent :child ;
}
var cmalizeRE = /-(\w)/g
function camelize(val){
return val.replace(cmalizeRE,function(c,m){
return m ? m.toUpperCase(): ""
})
}
function normalizeProps(options){
var props = options.props
if(!props){
return
}
var i , val , name
var res = {}
if(Array.isArray(props)){
i = props.length
while(i--){
val = props[i]
if(toString.call(val) === '[object String]'){
name = camelize(val)
res[name] = {type: null}
}else{
warn('使用数组愈发时props成员' +val+ '必须时一个数组')
} }
}else if(isPlainObject(props)){
for(var key in props){
val = props[key]
name = camelize(key)
res[name] = isPlainObject(val) ? val : {type: val}
}
}else {
warn('选项props的值必须是一个对象或者是数组')
}
options.props = res
}
function mormalizeDirectives(options){
var dir = options.directives
var res = {}
if(!dir){
return
}
if(dir){
for(var key in dir){
var val = dir[key]
var name = camelize(key)
if(isPlainObject(val)){
res[name] = val
}
if(toString.call(val) === '[object Function]'){
res[name] = {
bind: val,
upata: val
}
}
}
}
options.directives = res }
function mergeOptions(parent,child,vm){
var options = {}
// 检测是component 是否是合法的
checkComonpents(child)
// 规范props
normalizeProps(child)
// 规范 dirctives
mormalizeDirectives(child) // console.log(parent, child)
for(key in parent){
magerField(key)
}
for(key in child){
if(!hasOwn(parent ,key)){ // parent 中循环过地方不进行循环
magerField(key) // ----忘记写
} }
// 默认合并策略
function magerField(key){
// 自定义策略 默认策略
// console.log(key)
var result = strats[key] || defaultStrat // ---忘记写
options[key] = result(parent[key],child[key] , key , vm)
}
// console.log(options)
return options
} var allowedGlobals = makeMap(
'Infinity,undefined,NaN,isFinite,isNaN,' +
'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +
'require' // for Webpack/Browserify
);
function isNative(Ctor){
return typeof Ctor !== undefined && /native code/.test(toString.call(Ctor))
}
var hasproxy = typeof Proxy !== undefined && isNative(Proxy)
var hasHeadler = {
has: function(target,key){
var val = key in target
// key 是否是全局对象 或者内置方法 _
var isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_')
if(!val && !isAllowed){
warnNonpresent(target,key)
}
return val || !isAllowed
}
}
var getHeadler = {
get: function(target,key){
if( typeof key === 'string' && !(key in target)){
warnNonpresent(target,key)
}
return target[key]
}
} // 数据代理
function initProxy(vm){
var options = vm.$options
// 判断是否是es6 是否存在Proxy
if(hasproxy){
// 渲染函数拦截那些操作。 1. has 查询 2. get 或者
var headler = options.render && options.render._withStripeed ?
getHeadler:
hasHeadler;
vm._renderPorxy= new proxy(vm,headler)
}else{
// 如果不支es6 Proxy
vm._renderPorxy = vm
}
} // 初始化当前实例的$children 和$parent 的指向
function initLifeCycle(vm){
var options = vm.$options
// 当前组件 父实例
var parent = options.parent // 组件的实例对象
// 是否抽象组件
if(parent && !parent.abstrat){
while(parent.$options.abstrat && parent.$parent){
parent = parent.$options.parent
}
parent.$children.push(vm)
}
vm.$parent = parent
vm.$root = parent ? parent.$root : vm; vm.$children = [];
vm.$refs = {}; vm._watcher = null;
vm._inactive = null;
vm._directInactive = false;
vm._isMounted = false; // 是否挂载
vm._isDestroyed = false; // 是否销毁
vm._isBeingDestroyed = false; // 是否正在销毁 }
function callHook(vm,hook){
var options = vm.$options
var obj = options[hook]
if(obj){
for(var i =0 ; i < obj.length ; i++){
obj[i].call(vm)
}
} }
function getData(data,vm){
return data.call(vm,vm)
} //共享的访问器对象
var sharedProperty = {
enumerable:true,
configurable:true,
get:noop,
set:noop
};
function proxy(vm,data,key){ sharedProperty.get = function(){
console.log("我监听到你访问了我")
return this[data][key]
},
sharedProperty.set =function(newVal){
console.log("我设置了data的值"+key+"==" +newVal)
this[data][key] = newVal }
Object.defineProperty(vm,key,sharedProperty)
}
function initData(vm){
var opts = vm.$options
var data = vm.$options.data
// 通过之前strats 里面合成好的数据,data是一个function ,为了独立数据调用的空间。拿到data的值。
data = vm._data = typeof data === 'function' ? getData(data,vm) : data || {}
if(!isPlainObject(data)){
data = {}
warn('data选项应该是object对象')
}
// data methods props 里面属性名称不能相同
var props = opts.props
var methods = opts.methods
for(let key in data){
if(props && hasOwn(props,key)){
warn("props " + key + "选项已经定义为data的属性.")
}else if(methods && hasOwn(methods,key)){
warn("methods: " + key + "选项已经定义为data的属性.")
}else if(!isReserved(key)){
proxy(vm,"_data",key)
} } }
function observer(){
// 相应试系统
}
function initState(vm){
var opts = vm.$options
// 初始化props
if(opts.props){
initProps(vm,opts.props)
}
// 初始化methods
if(opts.methods){
initMethods(vm,opts.methods)
}
// 初始化computed
if(opts.computed){
initComputed(vm,opts.computed)
}
// 初始化data 如果存在就initData
if(opts.data ){
initData(vm)
}else{
// 放在响应式系统里面
observer(vm._data={},true)
}
}
function initMinxin(options){
Vue.prototype._init = function(options){
var vm = this
// 记录生成的vue实例对象
vm._uip = uip++ // //-------忘记写
//合并选项
vm.$options =mergeOptions(resolveConstructorOptions(vm.constructor),options,vm)
// // 初始化数值代理
initProxy(vm)
// 初始化当前实例的$children 和$parent 的指向
initLifeCycle(vm)
// 调用beforeCreate 的钩子函数
callHook(vm, 'beforeCreate')
// 初始化数据
initState(vm)
}
}
function Vue(options){
// 安全机制
if(!(this instanceof Vue)){ //-------忘记写
warn('Vue是一个构造函数,必须是由new关键字调用')
}
this._init(options)
}
initMinxin() // 初始化选项1: 规范 2: 合并策略。
Vue.options = {
components: {
transtions: {},
keepAlive:{},
solt:{},
transtionsGroup:{}
},
directives:{},
_bash: Vue
}
function initExend(Vue){
Vue.extend = function(extendOptions){
extendOptions = extendOptions || {} // -----忘记写
var Super = this
var Child = function VueComponent(options) {
this._init(options)
}
Child.prototype = Object.create(Super.prototype)
Child.prototype.constructor = Child // 改变constructor 的指向
Child.options = mergeOptions(Super.options,extendOptions)
// 子类继承父类的静态方法。
Child.extend = Vue.extend
// console.log(new Child({}))
return Child
}
}
initExend(Vue)
return Vue
})
dome.html 代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>第五课</title>
</head>
<body>
<div id="app">
<huml></huml>
</div>
<script src="vue.js"></script>
<!-- <script src="vue2.5.1.js"></script> -->
<script type="text/javascript">
var componentA = {
el: "#app"
}
var vm = new Vue({
el:"#app",
data: {
message: "hello Vue",
key: "wodow",
test: 1
},
beforeCreate: function(){
console.log('我钩子函数beforeCreate')
},
components:{
humle: componentA
} }) vm.test = 2;
console.log(vm)
</script>
</body>
</html>
【js】vue 2.5.1 源码学习 (七) 初始化之 initState 响应式系统基本思路的更多相关文章
- 如何实现全屏遮罩(附Vue.extend和el-message源码学习)
[Vue]如何实现全屏遮罩(附Vue.extend和el-message源码学习) 在做个人项目的时候需要做一个类似于电子相册浏览的控件,实现过程中首先要实现全局遮罩,结合自己的思路并阅读了(饿了么) ...
- 【js】 vue 2.5.1 源码学习(五) props directives规范化 props 合并策略
大体思路 (四) 上节回顾: A: 对于生命周期函数将父子组件的函数放到一个数组里面,特定时间点调用,保证父子组件函数都调用到. B: 对于directive,filters,components 等 ...
- 【js】vue 2.5.1 源码学习 (四) 钩子函数 资源选项 watch 的合并策略
大体思路 (三) 1.钩子函数 自定义策略 LIFECYCLE_HOOKS= [] created = [function(){} , function(){}] 组装方法 ...
- 【js】vue 2.5.1 源码学习 (三) Vue.extend 和 data的合并策略
大体思路 (三) 1. 子类父类 2.Vue.extend() //创建vue的子类 组件的语法器 Vue.extend(options) Profile().$mount('#app' ...
- 【js】vue 2.5.1 源码学习 (十) $mount 挂载函数的实现
大体思路(九) 本节内容: 1. $mount 挂载函数的实现. // 将Vue.prototype.$mount 缓存下来 ==>mountComponet(this,el) { // 组建挂 ...
- 【js】vue 2.5.1 源码学习 (九) 响应数组对象的变
大体思路(八) 本节内容: 1.Observe 如何响应数组的变化 代理原型 数组变异方法 shell cacheArrProto methods 新添加的数组需要加到显示系统里面,拦截 push等的 ...
- 【js】Vue 2.5.1 源码学习 (八)响应式入口observe
大体思路(七) 本节内容: deps 依赖收集的数组对象 => Dep 构造函数 /** ==> observe() * var ob * ==> if --isObject * = ...
- 【js】 vue 2.5.1 源码学习(六) initProxy initLifeCycle 渲染函数的作用域代理
大体思路 (五) 1. initProxy 渲染函数的作用域代理 ==> es6 如果支持proxy (hasProxy) 就用proxy 不支持就用 defineProperty() prox ...
- Vue源码学习02 初始化模块init.js
接上篇,我们看到了VUE分了很多模块(initMixin()stateMixin()eventsMixin()lifecycleMixin()renderMixin()),通过使用Mixin模式,都是 ...
随机推荐
- ie6中兼容性问题总结
针对firefox ie6 ie7 ie8的css样式中的line-height属性,以前我们大部分都是用!important来hack,对于ie6和firefox测试可以正常显示,但是ie7以上对! ...
- Codeforces 442A
题目链接 A. Borya and Hanabi time limit per test 2 seconds memory limit per test 256 megabytes input sta ...
- 封装函数通过输入(元素,属性,目标值)改变div样式
## 假设一个div样式如下```html<!DOCTYPE html><html lang="en"> <head> <meta cha ...
- LeetCode169 Majority Element, LintCode47 Majority Number II, LeetCode229 Majority Element II, LintCode48 Majority Number III
LeetCode169. Majority Element Given an array of size n, find the majority element. The majority elem ...
- 【Leetcode 堆、快速选择、Top-K问题 BFPRT】有序矩阵中第K小的元素(378)
题目 给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素. 请注意,它是排序后的第k小元素,而不是第k个元素. 示例: matrix = [ [ 1, 5, 9], [ ...
- jQuery迷你帮助查找功能
在线演示 本地下载
- day39-Spring 13-Spring的JDBC模板:默认连接池的配置
Spring内置的连接池DriverManagerDataSource的源码. /* * Copyright 2002-2008 the original author or authors. * * ...
- ABP 重写主键ID 多表查询ID无效
1.重写ID [Column("数据库指定的ID")] [Column("CarTypeID")] public override int Id { get; ...
- 集合--List&&ArrayList-LinkedList
1.8新特性 List接口中的replaceAll()方法,替换指定的元素,函数式接口编程 List 元素是有序的并且可以重复 四种add();方法 ArrayList(用于查询操作),底层是数组 ...
- poj 2184 01背包变形【背包dp】
POJ 2184 Cow Exhibition Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 14657 Accepte ...