vue相关( 适合先接触react 然后再接触vue的 )
vue 中的自定义事件
父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,应该怎样做?那就是自定义事件!
每个vue实例都有触发事件的方法
使用 $on(eventName) 监听事件
使用 $emit(eventName) 触发事件
父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。然后再对应子组件方法执行处触发事件,两者缺一不可
in父组件
<template>
<HelloWorld msg="Welcome to Your Vue.js App" v-on:son_method="father_method"/>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
name: "app",
components: {
HelloWorld
},
methods:{
father_method(data){
alert("father"+data);
}
}
};
</script>
in子组件
<template>
<div class="hello" v-on:click="son_method">
{{msg}}
</div>
</template>
<script>
export default {
name: "HelloWorld",
props:{
msg:{
type:String,
default: ""
}
},
methods:{
son_method: function(){
this.counter += 1;
this.$emit('son_method',"childData");
}
}
};
</script>
vue中监听props的改变
在react中 如果子组件的props使用的是父组件的state 中的值 那么当父组件中的state改变的时候 默认子组件是会重新render的 这也是面试环节react 性能优化中最常见的问题 那么到了vue 中一切都变了 ,当我们更新父组件的state 的时候惊讶的发现 子组件竟然没任何反应 这里解决这一问题的一个最简单的办法就是使用 使用vue 的computed 属性
props:{
msg:{
type:String,
default: ""
}
},
computed:{
message(){
return this.msg;
}
}
这样当父组件的porps改变的时候子组件也会监听到并且重新渲染
响应式原理
Vue.js的响应式原理依赖于Object.defineProperty
尤大大在Vue.js文档中就已经提到过,这是Vue.js不支持IE8 以及更低版本浏览器的原因。
每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
一、简单的认知
将数据data变成可观察的
const definedReactive = (obj,key,value,cb) =>{
Object.defineProperty(obj,key,{
enumerable: true,
configurable: true,
get(){
return value;
},
set(newVal){
value = newVal;
cb();
}
})
}
const observe = (data,cb)=> {
Object.keys(data).forEach((item)=>definedReactive(data,item,data[item],cb))
}
class Vue{
constructor(options){
this._data = options.data;
observe(this._data,options.render);
}
}
let app = new Vue({
el: "#app",
data:{
text: "text",
text2: "text2",
},
render(){
console.log('render');
}
});
代理
我们可以在Vue的构造函数constructor中为data执行一个代理proxy。这样我们就把data上面的属性代理到了vm实例上。
_proxy.call(this, options.data);/*构造函数中*/
/*代理*/
function _proxy (data) {
const that = this;
Object.keys(data).forEach(key => {
Object.defineProperty(that, key, {
configurable: true,
enumerable: true,
get: function proxyGetter () {
return that._data[key];
},
set: function proxySetter (val) {
that._data[key] = val;
}
})
});
}
我们就可以用app.text代替app._data.text了。
##二、继续探索
Object.defineProperty
Object.defineProperty 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象,先来看一下它的语法:
Object.defineProperty(obj, prop, descriptor);
obj 是要在其上定义属性的对象;prop 是要定义或修改的属性的名称;descriptor 是将被定义或修改的属性描述符。
比较核心的是 descriptor,它有很多可选键值,具体的可以去参阅它的文档。这里我们最关心的是 get 和 set,get 是一个给属性提供的 getter 方法,当我们访问了该属性的时候会触发 getter 方法;set 是一个给属性提供的 setter 方法,当我们对该属性做修改的时候会触发 setter 方法。
一旦对象拥有了 getter 和 setter,我们可以简单地把这个对象称为响应式对象。那么 Vue.js 把哪些对象变成了响应式对象了呢,接下来我们从源码层面分析。
initState
在 Vue 的初始化阶段,_init 方法执行的时候,会执行 initState(vm) 方法,它的定义在 src/core/instance/state.js 中。
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)
}
}
initState 方法主要是对 props、methods、data、computed 和 wathcer 等属性做了初始化操作。这里我们重点分析 props 和 data;
- initProps
function initProps (vm: Component, propsOptions: Object) {
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
// cache prop keys so that future props updates can iterate using Array
// instead of dynamic object key enumeration.
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
// root instance props should be converted
if (!isRoot) {
toggleObserving(false)
}
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
const hyphenatedKey = hyphenate(key)
if (isReservedAttribute(hyphenatedKey) ||
config.isReservedAttr(hyphenatedKey)) {
warn(
`"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
vm
)
}
defineReactive(props, key, value, () => {
if (vm.$parent && !isUpdatingChildComponent) {
warn(
`Avoid mutating a prop directly since the value will be ` +
`overwritten whenever the parent component re-renders. ` +
`Instead, use a data or computed property based on the prop's ` +
`value. Prop being mutated: "${key}"`,
vm
)
}
})
} else {
defineReactive(props, key, value)
}
// static props are already proxied on the component's prototype
// during Vue.extend(). We only need to proxy props defined at
// instantiation here.
if (!(key in vm)){
proxy(vm, `_props`, key)
}
}
toggleObserving(true)
}
props 的初始化主要过程,就是遍历定义的 props 配置。遍历的过程主要做两件事情:一个是调用 defineReactive 方法把每个 prop 对应的值变成响应式,可以通过 vm._props.xxx 访问到定义 props 中对应的属性。