Vue核心 配置代理与插槽


更新记录

  • 2023-09-14:对于“slot插槽”作了补充。

Vue脚手架配置代理

vue.config.js是一个可选的配置文件,如果项目的根目录中存在这个文件,那么它会被@vue/cli-service自动加载。你也可以使用package.json中的vue字段,但是注意这种写法需要你严格遵照JSON的格式来写。

方法一

vue.config.js中添加如下配置

module.exports = {
	devServer:{
		proxy:"http://localhost:5000"
	}
}

说明

  1. 优点:配置简单,请求资源时直接发给前端(8080)即可。
  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理
  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,才会将请求会转发给服务器(优先匹配前端资源)

方法二

编写vue.config.js配置具体代理规则

module.exports = {
devServer: {
    proxy: {
      '/api1': {                              //匹配所有以'/api1'开头的请求路径
        target: 'http://localhost:5000',      //代理目标的基础路径
	   pathRewrite:{'^/api1':''},            //在请求路径中去除路径前面的'/api1'
        // ws: true,                          //用于支持websocket
        // changeOrigin: true //用于控制请求头中的host值
        /**
         * changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
         * changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
         * changeOrigin默认值为false,但我们一般将changeOrigin值设为true
         */
      }
    }
  }
}

说明

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理
  2. 缺点:配置略微繁琐,请求资源必须加前缀

slot插槽

让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于父组件 ===> 子组件,即父组件给子组件的指定位置传递了数据。

使用场景

通过插槽可以让用户可以拓展组件,去更好地复用组件和对其做定制化处理

如果父组件在使用到一个复用组件的时候,获取这个组件在不同的地方有少量的更改,如果去重写组件是一件不明智的事情

通过slot插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用

比如布局组件、表格列、下拉选、弹框显示内容等

分类

  • 默认插槽
  • 具名插槽
  • 作用域插槽

使用方式:

  • 默认插槽

    父组件中:
    <Category>
    	<div>html结构1</div>
    </Category>
    子组件Category中:
    	<template>
    		<div>
    			<!--定义插槽-->
    			<slot>插槽默认内容</slot>
    		</div>
    	</template>
  • 具名插槽

    父组件指明放入子组件的哪个插槽slot="footer",如果是template可以写成v-slot:footer

    父组件中:
    		<Category>
    			<template slot="center">
    				<div>html结构1</div>
    			</template>
    			
    			<template slot="footer">
    				<div>html结构1</div>
    			</template>
    		</Category>
    子组件中:
    		<template>
    				<div>
    					<slot name="center">插槽默认内容...</slot>
    					<slot name="footer">插槽默认内容...</slot>
    				</div>
    		</template>
  • 作用域插槽

    scope用于父组件往子组件插槽放的html结构接收子组件的数据

    理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。

    父组件中:
    		<Category>
    			<template slot="scopeData">
    				<ul>
    					<li v-for="g in scopeData.games" :key="g">{{g}}</li>
    				</ul>
    			</template>
    		</Category>
    子组件中:
    		<template>
    			<div>
    				<slot :games='games'></slot>
    			</div>
    		</template>
    		
    		<script>
    			export default{
    				name:'Category',
    				props:['title'],
    				data(){
    					return {
    						games:['红色警戒','穿越火线']
    					}
    				}
    			}
    		</script>

小结

  • v-slot属性只能在<template>上使用,但在只有默认插槽时可以在组件标签上使用
  • 默认插槽名为default,可以省略default直接写v-slot
  • 缩写为#时不能不写参数,写成#default
  • 可以通过解构获取v-slot={user},还可以重命名v-slot="{user: newName}"和定义默认值v-slot="{user = '默认值'}"

原理分析

slot本质上是返回VNode的函数,一般情况下,Vue中的组件要渲染到页面上需要经过template -> render function -> VNode -> DOM 过程,这里看看slot如何实现:

编写一个buttonCounter组件,使用匿名插槽

Vue.component('button-counter', {
  template: '<div> <slot>我是默认内容</slot></div>'
})

使用该组件

new Vue({
    el: '#app',
    template: '<button-counter><span>我是slot传入内容</span></button-counter>',
    components:{buttonCounter}
})

获取buttonCounter组件渲染函数

(function anonymous(
) {
with(this){return _c('div',[_t("default",[_v("我是默认内容")])],2)}
})

_v表示穿件普通文本节点,_t表示渲染插槽的函数

渲染插槽函数renderSlot(做了简化)

function renderSlot (
  name,
  fallback,
  props,
  bindObject
) {
  // 得到渲染插槽内容的函数    
  var scopedSlotFn = this.$scopedSlots[name];
  var nodes;
  // 如果存在插槽渲染函数,则执行插槽渲染函数,生成nodes节点返回
  // 否则使用默认值
  nodes = scopedSlotFn(props) || fallback;
  return nodes;
}

name属性表示定义插槽的名字,默认值为defaultfallback表示子组件中的slot节点的默认值

关于this.$scopredSlots是什么,我们可以先看看vm.slot

function initRender (vm) {
  ...
  vm.$slots = resolveSlots(options._renderChildren, renderContext);
  ...
}
resolveSlots`函数会对`children`节点做归类和过滤处理,返回`slots
function resolveSlots (
    children,
    context
  ) {
    if (!children || !children.length) {
      return {}
    }
    var slots = {};
    for (var i = 0, l = children.length; i < l; i++) {
      var child = children[i];
      var data = child.data;
      // remove slot attribute if the node is resolved as a Vue slot node
      if (data && data.attrs && data.attrs.slot) {
        delete data.attrs.slot;
      }
      // named slots should only be respected if the vnode was rendered in the
      // same context.
      if ((child.context === context || child.fnContext === context) &&
        data && data.slot != null
      ) {
        // 如果slot存在(slot="header") 则拿对应的值作为key
        var name = data.slot;
        var slot = (slots[name] || (slots[name] = []));
        // 如果是tempalte元素 则把template的children添加进数组中,这也就是为什么你写的template标签并不会渲染成另一个标签到页面
        if (child.tag === 'template') {
          slot.push.apply(slot, child.children || []);
        } else {
          slot.push(child);
        }
      } else {
        // 如果没有就默认是default
        (slots.default || (slots.default = [])).push(child);
      }
    }
    // ignore slots that contains only whitespace
    for (var name$1 in slots) {
      if (slots[name$1].every(isWhitespace)) {
        delete slots[name$1];
      }
    }
    return slots
}
_render`渲染函数通过`normalizeScopedSlots`得到`vm.$scopedSlots
vm.$scopedSlots = normalizeScopedSlots(
  _parentVnode.data.scopedSlots,
  vm.$slots,
  vm.$scopedSlots
);

作用域插槽中父组件能够得到子组件的值是因为在renderSlot的时候执行会传入props,也就是上述_t第三个参数,父组件则能够得到子组件传递过来的值


文章作者: QT-7274
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 QT-7274 !
评论
  目录