欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

vue相关( 适合先接触react 然后再接触vue的 )

程序员文章站 2022-05-31 17:33:51
...

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 中对应的属性。

相关标签: vu e