一、vue
-
provide, inject
provide选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性。
inject选项应该是:
- 一个字符串,或
- 一个对象,对象的key是本地的绑定名,value是:
- 在可用的注入内容中搜索用的key(字符串或Symbol)或
- 一个对象,该对象的:
- from 属性是在可用的注入内容中搜索用的key
- default 属性时降级情况下使用的value
// 父组件 provide:{ } // 子组件 inject:{ foo:{ from:'foo', default:'test' } }, mounted(){ console.log(this.foo); // test } <!--另外一种写法--> // 子组件 inject:['foo']; mounted(){ console.log(this.foo);// test }
-
el
提供一个在页面上已经存在的DOM元素作为Vue实例的挂载目标。可以是CSS选择器,也可以是一个HTMLElement。
在实例挂载之后,元素可以用vm.mount()手动开启编译
源代码:
if (vm.$options.el) { vm.$mount(vm.$options.el) }
var app = new Vue({ data:{ message:'hello Vue!' }, el:'#bpp', directives:{ }, provide(){ return { getData(){ console.log('123') } } }, beforeCreate(){ console.log(this.message) }, mounted(){ console.log(this._data, this._provided) } }); // 在 id= bpp的元素上加载Vue实例
数组去重
function dedupe (latest, sealed) {
// compare latest and sealed to ensure lifecycle hooks won't be duplicated
// between merges
if (Array.isArray(latest)) {
const res = []
sealed = Array.isArray(sealed) ? sealed : [sealed]
for (let i = 0; i < latest.length; i++) {
if (sealed.indexOf(latest[i]) < 0) {
res.push(latest[i])
}
}
return res
} else {
return latest
}
}
- compositionstart compositionend
- compositionstart 事件触发于一段文字的输入之前(类似于 keydown 事件,但是该事件仅在若干可见字符的输入之前,而这些可见字符的输入可能需要一连串的键盘操作、语音识别或者点击输入法的备选词)。
- compositionend 事件触发一段文字输入完成或者删除文字时
<input type="text" id="text" />
function trigger(el,type){
const e = document.createEvent('HTMLEvents');
e.initEvent(type,true,true);
el.dispatchEvent(e);
}
var dom = document.getElementById('text');
dom.addEventListener('compositionstart', onCompositionStart)
dom.addEventListener('compositionend', onCompositionEnd)
function onCompositionStart(e){
e.target.composing = true
console.log('===========================');
console.log('e',e);
console.log('===========================');
}
function onCompositionEnd(e){
e.target.composing = false
console.log('===========================');
console.log('end',e);
console.log('===========================');
trigger(dom,'ceshi');
}
dom.addEventListener('ceshi',function(value){
console.log(value)
})
二. 源码分析
1.入口文件
package.json
scripts:{
"dev": "rollup -w -c build/config.js --environment TARGET:web-full-dev",
}
build/config.js
TARGET: web-full-dev
// Runtime+compiler development build (Browser)
'web-full-dev': {
entry: path.resolve(__dirname, '../src/entries/web-runtime-with-compiler.js'),
dest: path.resolve(__dirname, '../dist/vue.js'),
format: 'umd',
env: 'development',
alias: { he: './entity-decoder' },
banner
},
if (process.env.TARGET) {
module.exports = genConfig(builds[process.env.TARGET])
}
entries/web-runtime-with-compiler.js
import Vue from './web-runtime'
web-runtime.js
import Vue from 'core/index'
core/index.js
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
initGlobalAPI(Vue)
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) // 添加_init方法
// 初始化一些数据,其中 beforeCreate钩子在initState()数据之前,导致在beforeCreate钩子中拿不到data或者props的值,create钩子中能拿到data或者props中的值
stateMixin(Vue) // 初始化 props,data,computed,watch 全部通过defineReactive变成响应式数据 添加$watch方法
eventsMixin(Vue) // 添加$on $off $emit方法
lifecycleMixin(Vue) // 添加_update方法,$forceUpdate方法,$destroy
renderMixin(Vue) // 添加 _render方法,添加$nextTick方法
initMixin()
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')
global-api/index.js
export function initGlobalAPI (Vue: GlobalAPI) {}
<!--添加工具函数-->
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
-
其中响应式数据核心:
Observe,Dep,Watcher,defineReactive- Obejct.defineProperty
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
语法:Object.defineProperty(obj, prop, descriptor)
参数
obj 要在其上定义属性的对象。 prop 要定义或修改的属性的名称。 descriptor 将被定义或修改的属性描述符。
注: configurable 为false时,属性不能被删除,为true时能被删除,默认值为false
- defineReactive
主要作用就是在get时将watcher对象放入Dep.subs数组中,set时去更新视图
简易实现(详细参考Vue源码)
function defineReactive(obj,key,val){ const dep = new Dep(); Object.defineProperty(obj,key,{ enumerable:true, configurable:true, get: function reactiveGetter(){ dep.addSub(Dep.target); return val; }, set: function reactiveSetter(newVal){ if(val == newVal){ return; } dep.nodify(); val = newVal; // cb(val); } }); }
- Observer 将现有数据变成可视化数据
简易实现
function observer(value){ if(!value || typeof value !='object'){ return ; } // 递归循环 for(let key in value){ if(Obejct.prototype.toString.call( value[key]) == '[Object object]'){ dgObj(value[key],key,value); }else{ defineReactive(value, key , value[key]); } } }
- Dep 主要作用收集watcher对象
简易实现
function Dep(){ // this.subs = []; } Dep.prototype ={ subs:[], addSub:function(subs){ this.subs.push(subs); }, nodify:function(){ this.subs.forEach(sub =>{ sub.update(); }); } }; Dep.target = null;
- Watcher 将Dep.target赋值,更新视图
function Watcher(){ Dep.target = this; } Watcher.prototype ={ update:function(){ console.log('视图更新了!!!!~~~'); } }
- Vue构造函数
简易实现
function Vue (options){ this._data = options.data; new Watcher(); observer(this._data); }
以上都是简单实现,并且有好多细节没有体现出来,例如:添加watcheer实例时会有id记录这个对象有没有存在于数组中,如果有就不会在添加了;在给对象赋值时VUE中也考虑到了如果当前对象存在自己的setget属性的情况,但是本例中没有考虑这种情况只是简单的赋值 等;详细细节可以去VUE源码中查看