Vue2-CLI 脚手架的使用

2022.04.29

render函数

import Vue from 'vue'

在main.js中直接引入vue,其实引入的是runtime运行版的vue,精简掉了模板解析器,所以不能使用template配置项。

但我们可以使用render函数实现模板解析:

render(createElement) {
  return createElement(App)
}

由于无需使用this,所以可以用箭头函数简写:

render: h => h(App)

render函数只用在 main.js 中解析模板即可,在 .vue 模块中的template,另有脚手架的模板解析器处理。

父组件给子组件传递数据

通过父组件给子组件传递props实现。

子组件给父组件传递数据

通过父组件给子组件传递函数类型的props,子组件中通过函数参数传递数据实现。

或者,通过父组件给子组件绑定自定义事件,子组件触发自定义事件实现:

父组件中绑定自定义事件 方式一:

<Demo @event="test"/>
<!-- <Demo v-on:event="test"/> -->

父组件中绑定自定义事件 方式二:

<Demo ref="demo"/>
......
mounted() {
  this.$refs.demo.$on('event', this.test)
}

子组件中触发自定义事件:

this.$emit('event', data)  // data为要给父组件传递的数据

子组件中解绑自定义事件:

this.$off('event')

注意点:

  • 自定义事件也可以使用事件修饰符,或$once等
  • 组件上也可以绑定原生DOM事件,但需要加上.native修饰符
  • 事件的回调函数写在父组件中
  • 通过ref方式绑定自定义事件时,回调函数要么写在methods里面,要么写成箭头函数,否则this指向会出问题

任意组件间通信

全局事件总线GlobalEventBus可以实现任意组件间的通信,但更常用在跨级组件间通信,以及同级组件间通信。

原理:构建出一个vm或vc实例对象作为全局事件总线,添加到合适的位置,使之可以被所有的组件访问到,再分别给这个全局事件总线绑定自定义事件,从而实现任意组件间的通信。

由于每次生成出来的VueComponent都是一个全新不同的构造函数,所以不能在VueComponent的原型对象上添加全局事件总线。又因为VueComponent.prototype.__proto__ === Vue.prototype的存在,所以可以在Vue的原型对象上添加全局事件总线,从而使得所有的vc都可以访问得到。

在main.js中安装全局事件总线:

new Vue({
  ......
  beforeCreate() {
    Vue.prototype.$bus = this  // $bus为全局事件总线,this为vm实例对象
  },
  ......
})

接受数据的组件中给$bus绑定自定义事件,并设置在此组件销毁后解绑该事件:

methods() {
  test(data) {......}  // data为要接受的数据
}
......
mounted() {
  this.$bus.$on('event',this.test)
}
beforeDestroy() {
  this.$bus.$off('event')
}

提供数据的组件中触发$bus上的自定义事件:

this.$bus.$emit('event', data)  // data为要传递的数据

任意组件间通信还可以通过 消息订阅与发布 实现,但需要引入第三方库,因此更常用全局事件总线。

Vue封装的过渡与动画

用于在插入、更新、移除DOM元素时,在合适的时候给元素添加样式类名。以下以过渡为例:

在样式中准备过渡,可以自己写也可以引入第三方样式库:

v-enter {
  /* 进入的起点 */
}
v-enter-to {
  /* 进入的终点 */
}
v-enter-active {
  /* 进入的过程中(持续时间、动画曲线等) */
}


v-leave {
  /* 离开的起点 */
}
v-leave-to {
  /* 离开的终点 */
}
v-leave-active {
  /* 离开的过程中(持续时间、动画曲线等) */
}

在结构中使用<transition>包裹要过渡的元素:

<transition>
  <!-- 需要过渡效果的元素 -->
</transition>

如果要为多个transition分别配置name属性,则样式中v-xxx要改为demoname-xxx

若同一个transition中有多个元素需要过渡,则需要改用<transition-group>包裹元素,且每个元素都需要指定key值。

配置代理服务器

通过在vue.config.js中配置代理服务器,使得代理服务器的地址和端口号与本机相同,可以解决浏览器与服务器之间通信的跨域问题。

简单配置代理:

devServer: {
  proxy: "http://localhost:5000"  //代理目标的基础路径
}

Tips:

  • Vue中请求资源,直接发给前端本机即可
  • 但这种方式不能配置多个代理,也不能灵活地控制请求是否走代理
  • 会优先匹配前端本机的资源,若本机不存在,才会走代理转发给目标服务器

具体配置代理规则:

devServer: {
  proxy: {
    '/api1': {  //匹配所有以/api1开头的请求路径
      target: 'http://localhost:5000',  //代理目标的基础路径
      changeOrigin: true,  //改变请求头的host地址为目标服务器的地址
      pathRewrite: {'^/api1': ''}  //去掉匹配路径前缀
    },
    '/api2': {  //匹配所有以/api2开头的请求路径
      target: 'http://localhost:5001',  //代理目标的基础路径
      changeOrigin: true,
      pathRewrite: {'^/api2': ''}
    }
  }
}

Tips:

  • 这种方式可以配置多个代理,且可以灵活地控制请求是否走代理
  • Vue中请求资源时必须加路径前缀,并在代理配置中去掉前缀

Vue中常用的两个Ajax库

  1. axios:尤雨溪亲自推荐,当今最常用
  2. vue-resource:Vue1.0时代遗留项目较多使用,当今已缺乏维护

slot插槽

插槽可以让父组件向子组件的指定位置插入HTML结构。它也是组件间的一种通信方式,适用于父组件给子组件传递数据,只不过传递的数据为HTML结构。

slot插槽分为:默认插槽,具名插槽,作用域插槽。

默认插槽:最简单直接的插槽类型。

<!-- 父组件中 -->
<Category>
  <div>html结构</div>
</Category>


<!-- 子组件中 -->
<template>
  <div>
    <slot>插槽默认内容...</slot>  
  </div>
</template>

具名插槽:具有名称的插槽,用于多个插槽共存。

<!-- 父组件中 -->
<Category>
  <template slot="center">  <!-- 老式写法 -->
    <div>html结构1</div>
  </template>

  <template v-slot:footer>   <!-- 新式写法 -->
    <div>html结构2</div>
  </template>
</Category>


<!-- 子组件中 -->
<template>
  <div>
    <slot name="center">插槽默认内容...</slot>
    <slot name="footer">插槽默认内容...</slot>
  </div>
</template>

作用域插槽:数据在子组件的自身,但根据数据生成的HTML结构需要组件的使用者(父组件)来决定。

<!-- 父组件中 -->
<Category>
  <template scope="scopeData">  <!-- 老式写法 -->
    <!-- 生成的是ul列表 -->
    <ul>
      <li v-for="g in scopeData.games" :key="g">{{g}}</li>
    </ul>
  </template>
</Category>

<Category>
  <template slot-scope="scopeData">  <!-- 新式写法 -->
    <!-- 生成的是h4标题 -->
    <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
  </template>
</Category>


<!-- 子组件中 -->
<template>
  <div>
    <slot :games="games"></slot>
  </div>
</template>

<script>
export default {
  name: "Category",
  //数据在子组件自身
  data() {
    return {
      games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
    };
  },
};
</script>

Tips:

  • scope="scopeData"收到的是一个对象形式,包含了插槽提供的数据
  • 作用域插槽也可以有名称,和具名插槽一样多个作用域插槽共存