Vue-基础入门(中)--Part.1(1-5)

155 阅读4分钟

1-1 组件的定义及复用性,局部组件和全局组件

Component Tree

把复杂的应用拆分成很多小的组件来进行维护

全局组件

<script>
  const app = Vue.createApp({
    template: `
      <div>
        <counter-parent />
        <counter />
      </div>
    `
  })
​
  app.component('counter-parent', {
    template:`
      <counter />
    `
  })
​
  app.component('counter', {
    data() {
      return{
        count: 0
      }
    },
    template: `
      <div @click="count += 1">{{count}}</div>
    `
  })
  const vm = app.mount('#root');
</script>

这个就是全局组件,假如我们根组件没有引用子组件

const app = Vue.createApp({
  template: `
    <div>
      hello Y
    </div>
  `
})

但是子组件任然占据根组件的内存,这个就是全局组件,只要定义了,处处可以使用,性能不高,但是使用起来简单

局部组件

<script>
  const Counter = {
    data() {
      return {
        count: 1
      }
    },
    template: `
      <div @click="count += 1">{{count}}</div>
    `
  }
​
  const app = Vue.createApp({
    components: {
      counter: Counter
    },
    template: `
      <div>
        <counter />
      </div>
    `
  })
  const vm = app.mount('#root');
</script>

引用一个局部组件需要用components来注册,局部组件,定义了,要注册之后才能使用,性能比较高,使用起来有些麻烦,建议大写字母开头,驼峰命名。

局部组件使用时,要做一个名字和组件间的映射对象,你不写映射,Vue 底层也会自动尝试帮你做映射

1-2 组件间传值及传值校验

之前做过的那个TodoList就有用到组件间的传值,先贴一个很简单的代码

<script>
  const Counter = {
    props: ['content'],
    template: `
      <div>{{content}}</div>
    `
  }
​
  const app = Vue.createApp({
    components: {
      counter: Counter
    },
    template: `
      <div>
        <counter content="hello Y" />
      </div>
    `
  })
  const vm = app.mount('#root');
</script>

这里父组件要传给子组件的值,子组件用props接收,但是这种传值是静态的,也可以说是固定的,传过去的值是String类型

<script>
  const Counter = {
    props: ['content'],
    template: `
      <div>{{typeof content}}</div>
    `
  }
​
  const app = Vue.createApp({
    components: {
      counter: Counter
    },
    template: `
      <div>
        <counter content="123" />
      </div>
    `
  })
  const vm = app.mount('#root');
</script>

image-20211112144937161.png

要动态传值,只需要在content前面加一个冒号就行了

  const app = Vue.createApp({
    data () {
      return {
        name: 'Jennie'
      }
    },
    components: {
      counter: Counter
    },
    template: `
      <div>
        <counter :content="name" />
      </div>
    `
  })

这样写就行了

对于子组件接收父组件传过来的值时,可以设置一些属性

  const Counter = {
    props: {
      content: {
        type: String,
        required: true,
        default: 'Jie',
        validator: function(value) {
          return value < 1000
        }
      }
    },
    template: `
      <div>{{content}}</div>
    `
  }

type设置content的类型,可以是StringBooleanArrayObjectFunctionSymbol

required:这个值是否是必传,如果设置为true,但是父组件没有传,后台就会警告

default:默认值,假如父组件没有传值,就会使用默认值,也可以用函数表示

  default: function() {
    return 'Jie'
  },

validator:数据校验

1-3 单项数据流的理解

先对上一节做一个补充

<script>
  const app = Vue.createApp({
    data () {
      return {
         a: 'aaaa',
         b: 'bbbb',
         c: 'cccc',
         d: 'dddd'
      }
    },
    template: `
      <div>
        <counter :a="a" :b="b" :c="c" :d="d" />
      </div>
    `
  })
​
  app.component('counter', {
    props:['a', 'b', 'c', 'd'],
    template: `
      <div>{{a}} - {{b}} - {{c}} - {{d}}</div>
    `
  })
  const vm = app.mount('#root');
</script>

显然!!这样子传值很麻烦,万一要传很多值,那要写一大堆,很臃肿,我们可以简化一下

  const app = Vue.createApp({
    data () {
      return {
        params:{
          a: 'aaaa',
          b: 'bbbb',
          c: 'cccc',
          d: 'dddd'
        }
      }
    },
    template: `
      <div>
        <counter v-bind="params" />
      </div>
    `
  })

等价于<counter :a="params.a" :b="params.b" :c="params.c" :d="params.d" />

还有一个需要注意到的地方是,我们在传值的时候,假如传的值命名很长,看下面的代码

<script>
  const app = Vue.createApp({
    data () {
      return {
        name: 'Jennie'
      }
    },
    template: `
      <div>
        <counter :name-abc="name" />
      </div>
    `
  })
​
  app.component('counter', {
    props:['nameAbc'],
    template: `
      <div>{{nameAbc}}</div>
    `
  })
  const vm = app.mount('#root');
</script>

也就是说,属性传值的时候,使用name-abc这种命名,接的时候,使用nameAbc命名。

好了进入正题!单向数据流是什么?先看一段代码

<script>
  const app = Vue.createApp({
    data () {
      return {
        count: 1
      }
    },
    template: `
      <div>
        <counter :count="count" />
      </div>
    `
  })
​
  app.component('counter', {
    props:['count'],
    template: `
      <div @click="count += 1">{{count}}</div>
    `
  })
  const vm = app.mount('#root');
</script>

父组件向子组件传count这个值,我们点击divcount加1。但是实际上我们点击div的时候,控制台会有警告,我们是不能直接修改父组件传过来的值的!!!为什么呢?

假设一下:

  <div>
    <counter :count="count" />
    <counter :count="count" />
    <counter :count="count" />
  </div>

如果能修改,我们点击第一个counter,后面两个的组件的值都会发生变化,会造成逻辑混乱,维护的时候也不知道是哪个组件改变的值(每个子组件应该是相互独立的,不应该存在数据耦合)

单项数据流的概念:子组件可以使用父组件传递过来的数据,但是绝对不能修改传递过来的数据

1-4 Non-Prop 属性是什么

先看一个代码

<script>
  const app = Vue.createApp({
    template: `
      <div>
        <counter msg="hello" />
      </div>
    `
  })
​
  app.component('counter', {
    template: `
      <div>Jennie</div>
    `
  })
  const vm = app.mount('#root');
</script>

子组件没有用Props接收父组件传来的值,那么这些数据会变成子组件最外层dom节点上的属性

image-20211112160213883.png

如果我们不想接收Non-Prop 属性,我们可以

app.component('counter', {
  inheritAttrs: false,
  template: `
    <div>Jennie</div>
  `
})

但是假如子组件是这样的:

app.component('counter', {
  template: `
    <div>Jennie</div>
    <div>Jennie</div>
    <div>Jennie</div>
  `
})

image-20211112160320937.png

因为子组件最外层的节点不止一个,属性不知道要挂在哪一个节点上,我们可以通过下面的方式来接收

<script>
  const app = Vue.createApp({
    template: `
      <div>
        <counter msg="hello" msg1="hello1" />
      </div>
    `
  })
​
  app.component('counter', {
    template: `
      <div v-bind="$attrs">Jennie</div>
      <div :msg="$attrs.msg">Jennie</div>
      <div :msg1="$attrs.msg1">Jennie</div>
    `
  })
  const vm = app.mount('#root');
</script>

image-20211112160714517.png

v-bind="$attrs":接收所有的Non-Prop属性

:msg="$attrs.msg":接收指定的Non-Prop属性

Non-Prop 属性的应用

可以设置style属性

<script>
  const app = Vue.createApp({
    template: `
      <div>
        <counter style="color: pink" />
      </div>
    `
  })
​
  app.component('counter', {
    template: `
      <div>Jennie</div>
    `
  })
  const vm = app.mount('#root');
</script>

image-20211112161043450.png

还可以绑定class属性

<style>
  .blue {
    color: blue;
  }
</style>
<script>
  const app = Vue.createApp({
    template: `
      <div>
        <counter class="blue" />
      </div>
    `
  })
​
  app.component('counter', {
    template: `
      <div>Jennie</div>
    `
  })
  const vm = app.mount('#root');
</script>

image-20211112161213728.png

1-5 父子组件如何通过事件进行通信

先看一个很简单的父子组件通过事件进行通信的例子:

<script>
  const app = Vue.createApp({
    data () {
      return {
        count: 1  
      }
    },
    methods:{
      handleAdd() {
        this.count += 1
      }
    },
    template: `
      <div>
        <counter :count="count" @add-one="handleAdd"/>
      </div>
    `
  })
​
  app.component('counter', {
    props:['count'],
    methods: {
      handleClick() {
        this.$emit('addOne')
      }
    },
    template: `
      <div @click="handleClick">{{count}}</div>
    `
  })
  const vm = app.mount('#root');
</script>

子组件通过$emit向父组件触发一个addOne事件,告诉父组件让他帮自己改count的值

这个$emit不仅可以触发事件,还能传递参数,也能写js表达式,举例^ ^

传递参数(参数可以不止一个^ ^):

// 子组件
handleClick() {
  this.$emit('addOne', 2)
}
// 父组件
handleAdd(num) {
  this.count += num
}

js表达式:

// 子组件
handleClick() {
  this.$emit('addOne', this.count + 2)
}
// 父组件
handleAdd(num) {
  this.count = num
}

还有就是我们可以这样写:

在子组件里写:emits:['addOne'],,这样不仅可以很直观的展示在这个组件里我们会向父组件触发哪些事件,还可以:

emits:{
  addOne: (num) => {
    if(num < 4) {
      return true
    } else {
      return false
    }
  }
}

这样写可以对$emit里传的值进行校验

最后说一个稍微“高级”的知识

前面我们用input框的时候已经知道v-model可以实现数据的双向绑定,那我们父子组件的通信能不能用v-model来实现捏?

当然是可以的 > <

<script>
  const app = Vue.createApp({
    data () {
      return {
        count: 1  
      }
    },
    template: `
      <counter v-model="count"/>
    `
  })
​
  app.component('counter', {
    props:['modelValue'],
    methods: {
      handleClick() {
        this.$emit('update:modelValue', this.modelValue + 2)
      }
    },
    template: `
      <div @click="handleClick">{{modelValue}}</div>
    `
  })
  const vm = app.mount('#root');
</script>

使用v-model传值时,就收的props里写modelValue是固定的!!比如写成modelValue1就会报错

$emit里面写update:modelValue也是固定的!!

但是这个modelValue也不是真的不能改,我们可以通过<counter v-model:add="count"/>,这样就可以把子组件里的modelValue换成add

这个知识点也不是特别常用,知道就好