精华内容
下载资源
问答
  • echarts vue组件封装
    2021-09-06 10:20:13

    echarts vue组件封装

    为什么封装echarts组件

    1、原生echarts不是vue组件风格的,不爽

    2、原生echarts需要操作dom,麻烦

    3、原生echarts没有响应式系统,太菜

    在vue项目中使用echarts,步骤如下:

    (1)安装echarts依赖

    npm install echarts -S 
    

    (2)引入echarts,可全局引入和按需引入

    全局引入:

    // 引入echarts  --- 在 main.js 中
    import echarts from 'echarts'
    Vue.prototype.$echarts = echarts 
    

    在 echarts.vue 中,初始化echarts实例进行绘制图形。

    this.charts = this.$echarts.init(document.getElementById(id));
    

    按需引入:

    //在 echarts.vue 中
    // 引入基本模板
    let echarts = require('echarts/lib/echarts')
    // 引入柱状图组件
    require('echarts/lib/chart/bar')
    // 引入提示框和title组件
    require('echarts/lib/component/tooltip')
    require('echarts/lib/component/title')
    

    (3)项目案例:

    1.实现方式:

    创建一个待渲染的dom

    <template>
      <div>
        <div id="myLine" :style="echartStyle"></div>
      </div>
    </template>
    

    绘制函数:

    // 绘制折线图
    drawLine(id){
      this.charts = this.$echarts.init(document.getElementById(id));
      this.charts.on("click", this.eConsole);
      this.charts.setOption({
      	title: {
      		left: 'center',
    	        text: this.titleText, //标题文本
    	    },
    	    tooltip: {
    	        trigger: 'axis'
    	    },
    	    legend: {
    	    	left: 'left',
    	        data: this.opinion // 区域名称
    	    },
    	    grid: {
    	        left: '3%',
    	        right: '4%',
    	        bottom: '3%',
    	        containLabel: true
    	    },
    	    toolbox: {
    	        feature: {
    	            saveAsImage: {}
    	        }
    	    },
    	    xAxis: {   // x 轴
    	        type: 'category',
    	        boundaryGap: false,
    	        data: this.x
    	    },
    	    yAxis: {   // y 轴
    	        type: 'value'
    	    },
    	    series: this.opinionData  // 区域数据
      })
    }
    

    初始化挂载到页面上:

    mounted(){
    	this.$nextTick(function() {
    		this.drawLine('myLine')
    	})
    },
    

    props:

    echartStyle: {  // 样式 - 1
    	type: Object,
    	default() {
    		return {}
    	}
    },
    titleText: {   //标题文本 - 1
    	type: String,
    	default: ''
    },
    
    opinion: {    //区域名称 - 1
    	type: Array,
    	default() {
    		return []
    	}
    },
    opinionData: {   // 区域数据 - 1
    	type: Array,
    	default() {
    		return []
    	}
    },
    x: {  // x 轴
    	type: Array,
    	default() {
    		return []
    	}
    
    }
    

    2.控件使用

    调用实例:

    <m-line
      :echartStyle="s"
      :titleText="title"
      :opinion="barName"
      :opinionData="info"
      :x="barX">
    </m-line>
    

    传递数据:

    import mLine from '../components/line' 
    export default {
        components: {
            mLine,
        },
    	data() {
    		return {
    	        s: {
    	          height: ''
    	        },
    	        title: '动态统计',
    	        barName: ['文档数', '任务数'],
    	        barX: ['2019/03/01','2019/03/02','2019/03/03','2019/03/04','2019/03/05','2019/03/06','2019/03/07'],
    	        info: [
    	  	        {
    	  	            name:'文档数',
    	  	            type:'line',
    	  	            stack: '总量',
    	  	            data:[120, 132, 101, 134, 90, 230, 210]
    
    	  	        },
    	  	        {
    	  	            name:'任务数',
    	  	            type:'line',
    	  	            stack: '总量',
    	  	            data:[220, 182, 191, 234, 290, 330, 310]
    	  	        }
    	  	    ],
    		}
    
    	},
    
    	created(){
    	  this.s.height = 300 + 'px'
    
    	},
    }
    

    这里主要传递四个参数到组件中,title、legend、series、xAxis,分别表示文章标题、区域名称、区域数据以及X轴坐标

    更多相关内容
  • vue 封装的 echarts 组件

    2019-03-05 17:05:35
    vue 封装的 echarts 组件,主要传递四个参数到组件中,title、legend、series、xAxis,分别表示文章标题、区域名称、区域数据以及X轴坐标。组件被同一个页面多次调用,数据被覆盖问题解决办法。
  • Vue 组件封装

    千次阅读 2019-06-16 05:35:11
    Vue 组件封装 项目中没有从零开始封装一个组件,本文记录一下 Vue 组件封装的基本实践和一些组件的相关知识。主要涉及以下知识点: 封装一个组件的代码组织形式; vue 组件的三大核心: 属性(props、data); 事件...

    Vue 组件封装

    项目中没有从零开始封装一个组件,本文记录一下 Vue 组件封装的基本实践和一些组件的相关知识。主要涉及以下知识点:

    • 封装一个组件的代码组织形式;
    • vue 组件的三大核心:
      • 属性(props、data);
      • 事件
      • 插槽
    • 样式
    • 其他一些杂项
      • $nextTick 函数的使用
      • 获取 DOM 元素及在父级组件中执行子组件方法

    使用第三方计数库 countup.js 创建一个 count-to 组件对以上知识进行总结。

    文件组织形式

    在组件文件夹 component 下创建一个与组件名相同的文件,文件夹内必须有 index.js,并将组件导入到该文件中,这样方便我们引用组件。

    count-to 文件夹内:

    //index.js
    import CountTo from './count-to.vue'
    export default CountTo
    复制代码

    使用组件时,只需这样引入:

    import CountTo from  "_c/count-to";// _c 是组件存放路径
    复制代码

    Vue 组件的三大核心

    属性(props、data 和样式)

    props 定义了组件可配置的数据,确定的组件的核心功能。封装组件时,props 推荐写成对象形式,方便对数据进行验证,提高了代码健壮性也能明确如何使用。

    常见的检查类型:NumberStringBooleanArrayObjectDateFunctionSymbol构造函数null|undefined 会通过所有类型。

    还可以自定义验证函数,指定是否必须和默认值。

    props:{
    	// 多个可能的类型
      propB: [String, Number],
    	// 必填的字符串
      propC: {
        type: String,
        required: true
      },
      // 带有默认值的数字
      propD: {
        type: Number,
        default: 100
      },
      // 带有默认值的对象
      propE: {
        type: Object,
        // 对象或数组默认值必须从一个工厂函数获取
        default: function () {
          return { message: 'hello' }
        }
      },
      // 自定义验证函数
      propF: {
        validator: function (value) {
          // 这个值必须匹配下列字符串中的一个
          return ['success', 'warning', 'danger'].indexOf(value) !== -1
        }
      }
    }
    复制代码

    通过阅读 countUP文档,了解到构造函数CountUp 的参数

    CountUp(eleDOM,startValue,endValue,decimals,duration,options);// eleDOM 是数值显示的元素;endValue 是数值的最终值,这两个参数必须的。
    复制代码

    组件代码如下:

    <template>
      <div>
        <span :id="eleId"></span>
      </div>
    </template>
    <script>
    import CountUp from "countup";
    export default {
      name: "CountTo",
      props: {
        /**
         * @description 起始值
         */
        startValue: {
          type: Number,
          default: 0
        },
    
        /**
         * @description 终止值
         */
        endValue: {
          type: Number,
          required: true
        },
        /**
         * @description 小数点后保留几位小数(精度)
         */
        decimals: {
          type: Number,
          default: 0
        },
        /**
         * @description 渐变时长(秒)
         */
        duration: {
          type: Number,
          default: 1
        },
        /**
         *@description 变速效果
         */
        useEasing: {
          type: Boolean,
          default: false
        },
        /**
         *@description 分组
         */
        useGrouping: {
          type: Boolean,
          default: true
        },
        /**
         *@description 分组符号 2,2234
         */
        separator: {
          type: String,
          default: ","
        },
        /**
         *@description 整数小数分隔符 34.56
         */
        decimal: {
          type: String,
          default: "."
        },
        /**
         * @description  动画延迟(秒)
         */
        delay: {
          type: Number,
          default: 0
        },
      },
      data() {
        return {};
      },
      computed: {
        eleId() {
          //使用 this.uid 生成全局唯一id
          return `count_up_uid${this._uid}`;
        },
      },
      mounted() {
        //TODO: this.$nextTick
        this.$nextTick(() => {
          let options = {
            useEasing: this.useEasing,
            useGrouping: this.useGrouping,
            separator: this.separator,
            decimal: this.decimal
          };
          this.counter = new CountUp(
            this.eleId,
            this.startValue,
            this.endValue,
            this.decimals,
            this.duration,
            options
          );
        });
      }
    };
    </script>
    复制代码

    代码说明: this._uid 用于生成组件内唯一的id值,可用作元素的id,值是递增的。 this.$nextTick 函数接收一个回调函数作为参数,回调函数会在 DOM更新 之后执行,如果某些操作必须在DOM更新之后,可将这些操作作为其参数。

    计数组件的基本功能就满足了。

    这样使用组件:

    <template>
    	<div>
    		<count-to :end-value="endValue" :decimals="decimals" :duration="5" title="这个会挂载到组件根元素上">
    		</count-to>
    	</div>
    </template>
    <script>
    	import CountTo from '_c/count-to'
    	export default {
    		name: 'count_to',
    		components: {
    			CountTo
    		},
    		data() {
    			return {
    				endValue: 4000,
    				decimals: 2,
    				className: '',
    			}
    		},
    	}
    </script>
    复制代码
    <count-to :end-value="endValue" :decimals="decimals" :duration="5"></count-to>
    复制代码

    prop 的命名:

    组件中使用小驼峰命名,传递值是使用-

    关于 props 传递静态值:

    不使用 v-bind 指令:传递的是静态值,是一个字符串字常量,而不是变量,而使用:指令传递的值,是有类型的。:duration="5" 传递是 数值 5,duration="5" 传递字符串5duration="true" 传递的是字符串true 而不是 Boolean 值真值。

    默认值:

    传递是引用类型的值(对象和数组),默认值需要使用一个工厂函数返回一个引用类型的值。

    inheritAttrs:

    如果传递一个组件中没有声明的属性,该属性会挂载都组件元素上,可在组件中将inheritAttrs 设置为 false 取消这一行为。上面的 title 属性会挂载到组件的 div 上。该属性不应 style 和 calss 的传递。

    <count-to title="会挂载到组件的根元素上" test="test" :end-value="endValue" :decimals="decimals" :duration="5">	</count-to>
    复制代码

    title 会成为count-to 组件的根元素的属性:

    <div title="这是标题" test="测试">
    	<span id="count_up_uid14" >10,000.00</span>
    </div>
    复制代码

    $attrs 接收没有声明的属性

    title 和 test 属性没有在组件中声明,依然可以在组件中使用 attrs 接收到些属性: <span>没有props接收的父组件数据:{{$attrs}}</span><br/>

    最后的结果:

    <div title="这是标题" test="测试">
    	<span>没有props接收的父组件数据:{
    		"title": "这是标题",
    		"test": "测试"
    	}</span><br>
    	<span id="count_up_uid14">10,000.00</span>
    </div>
    复制代码

    inheritAttrs: false 和 $attrs 结合使用:

    有了 inheritAttrs: false 和 $attrs,你就可以手动决定这些特性会被赋予哪个元素,而不需要声明变量接收

    {% raw %}

    See the Pen $attrs使用 by JackZhouMine (@JackZhouMine) on CodePen.

    {% endraw %}

    data vs props

    props 从父级组件入,传入的值由父级组件维护,不允许在子组件中直接操作,是否必需和数据类型都是确定的,我们不能改变。

    data 是组件内部维护的状态,组件可直接操作,可随时改变值、类型等。

    相同点:都是组件的属性,改变两者都会响应到模板上。

    打破 props 单向数据流

    Vue 不允许在子组件中直接操作 props ,否则会报错,因为父组件和子组件都可直接操作 props,会使得 props 的管理变得混乱。可通过一些间接的方式操作 props:

    1. 将 props 赋值给 data ,然后操作 data;
    2. 在计算属性中返回 props;

    以上两种方式,修改后的值,是不能会响应到父组件的,想要在父级组件中也看到修改,需要用到下面的方式:

    1. .sync 和 $emit 结合

    传递props 时加上 .sync 修饰符,在子组件内部使用 $emit 更新 props。

    使用 .sync 需要注意:

    • 不能和表达式一起使用:v-bind:title.sync="doc.title + '!'";
    • 不能传递对象字面量:v-bind.sync="{ title: doc.title }"
    1. 传递引用类型的 props

    传递数组和对象,在子组件中修改他们,会直接反应到父组件上。

    事件

    传统的web开发使用事件驱动:

    • 查询节点→绑定事件监听;
    • 用在页面上触发事件→执行监听器,修改DOM,反馈到页面上; 这种模式开发效率低成本高。

    Vue 的核心思想是数据驱动,视图由数据决定。MVVM 架构的页面变化流程:

    View(用户操作) → 执行 DOMlistenrs (ViewModel) → Data 改变 (Model)→ View 改变。

    组件和绑定原生事件和自定义事件,绑定原生事件时,需要添加native修饰符。

    可以在组件的原生事件处理器中触发一个自定义事件,就能在父级组件中监听该事件,执行相关操作。

    count-to 声明一个 changeValue 事件:

    增加一个按钮:

    <button @click="add">+</button>
    复制代码

    在事件处理器add中触发一个自定义事件:

    add() {
    	this.$emit("changeValue", Math.random() * 100);
    }
    复制代码

    $emit 的第一个参数是事件名称,第二个参数是传递到该事件监听器的参数。

    在组件上监听 changValue:

    <template>
    	<div>
    		<count-to :end-value="endValue" :decimals="decimals" :duration="5" @changeValue="changeValue">
    		</count-to>
    	</div>
    </template>
    <script>
    	import CountTo from '_c/count-to'
    	export default {
    		name: 'count_to',
    		components: {
    			CountTo
    		},
    		data() {
    			return {
    				endValue: 4000,
    				decimals: 2,
    			}
    		},
    		methods: {
    			changeValue(value) {
    				this.endValue += value
    			}
    		},
    	}
    </script>
    复制代码

    自定义一个更新结束事件:

    <script>
    import CountUp from "countup";
    export default {
      name: "CountTo",
      methods: {
        getCount() {
          //使用 id 获取 DOM
          let span = document.getElementById(this.eleId);
          let currentValue = Number.parseFloat(span.innerText.split(",").join(""));
          return currentValue.toFixed(this.decimals);
        },
        emitEnd() {
          this.$emit("on-end", this.getCount());
          // this.$emit('on-end', this.endValue) 使用 endValue 不是 库处理后的值,所有使用 DOM 元素获取更新后的值
        },
      },
      // 监听 props 属性的变化
      watch: {
        endValue(newValue) {
          //update 是库的方法
          this.counter.update(newValue);
          setTimeout(() => {
            this.emitEnd();
          }, this.duration * 1000 + 2);
        }
      }
    };
    </script>
    复制代码

    在组件上使用监听on-end:

    <template>
    	<div>
    		<count-to :end-value="endValue" :decimals="decimals" :duration="5" @on-end="endUp">
    		</count-to>
    	</div>
    </template>
    <script>
    	import CountTo from '_c/count-to'
    	export default {
    		name: 'count_to',
    		components: {
    			CountTo
    		},
    		data() {
    			return {
    				endValue: 4000,
    				decimals: 2,
    			}
    		},
    		methods: {
    			// 更新接收后,会触发自定义事件,然后执行该函数
    			endUp(value) {
    				console.log('endValue => ', value);
    			},
    		},
    	}
    </script>
    复制代码

    表单修饰符

    • lazy : 在change事件同步数据;
    • trim : 删除首尾空格;
    • number :只能输入数字;

    事件修饰符

    • stop:阻止冒泡;
    • prevent :阻止默认行为;
    <!-- 阻止单击事件继续传播 -->
    <a v-on :click.stop="doThis"></a>
    <!-- 提交事件不再重载页面 -->
    <form v-on :submit.prevent="onSubmit"></form>
    <!-- 修饰符可以串联 -->
    <a v-on:click.stop.prevent="doThat"></a>
    复制代码

    插槽

    props 传递普通的数据类型,插槽提供了传递 HTML 代码的方式,父组件中给的插槽内容,会被放置到子组件的指定为位置。

    父组件决定是否显示插槽和怎样显示,子组件决定插槽显示的位置。

    三种插槽:

    • 匿名插槽;
    • 命名插槽;
    • 作用域插槽。

    我们现在想要在 数值左边显示一个从父级组件传递到组件中的文字提示,数值右边显示人民币符号。

    可使用插槽接收文字提示和人民币符号:

    <template>
      <div>
    	<!-- 匿名插槽 找不到放置的位置,就放在这里-->
        <slot></slot>
        <span :id="eleId"></span>
        <slot name="right"></slot>
    		<!-- 命名插槽-->
      </div>
    </template>
    复制代码

    在父级组件传递插槽内容:

    <template>
    	<div>
    		<count-to :end-value="endValue" :decimals="decimals" :duration="5">
    			<span>金额:</span>
    			<span slot="right"></span>
    		</count-to>
    	</div>
    </template>
    复制代码

    最后的html是这样的:

    <div>
    	<span>金额:</span>
    	<span id="count_up_uid13" >4,000.00</span>
    	<span></span>
    </div>
    复制代码

    不传递插槽内容时,可以在组件中设置一个默认的插槽内容:

    <template>
      <div>
        <slot>奖金额度:</slot>
        <span :id="eleId"></span>
        <slot name="right"></slot>
      </div>
    </template>
    复制代码

    父级组件的作用域和子组件的作用是独立的,在父级组件的插槽内容中,获取不到子组件的数据。

    <template>
    	<div>
    		<count-to :end-value="endValue" :decimals="parentDecimals" :duration="5">
    			<span>精确到几位小数:{{parentDecimals}}</span>
    			<span slot="right">{{decimals}}</span>
    		</count-to>
    	</div>
    </template>
    复制代码

    parentDecimals 是父级组件中的属性,插槽内容属于父级作用域,可获取父级的数据; decimals 是子级组件中的属性,插槽内容属于父级作用域,获取不到值;

    想要在父级插槽内容中获取子组件的数据,就需要用到作用域插槽。

    现在想要把数值前面的文字从父级组件传递到子组件,并且还要传递文字的颜色:

    text: {
    	name: "本月工资",
    	color: "#F4D03F"
    },
    复制代码

    子组件这样定义:

    <template>
      <div>
    		<!--向父级组件传递text 并起了名字-->
        <slot v-bind="text" name="left">奖金额度:</slot>
        <span :id="eleId" ref="number"></span>
        <slot name="right"></slot>
      </div>
    </template>
    <script>
    import CountUp from "countup";
    export default {
      name: "CountTo",
      props: {
    	//增加 prop 
    	text:{
    		type:Object,
    		default:()=>{}
    	},
      }
    };
    </script>
    复制代码

    这样使用组件:

    <template>
      <div>
        <count-to
          :end-value="endValue"
          :decimals="decimals"
          :duration="5"
          :text="text"
        >
          <template slot-scope="data" slot="left">
            <span :style="{color:data.color}">{{data.name}}:</span>
          </template>
          <span slot="right"></span>
        </count-to>
      </div>
    </template>
    <script>
    import CountTo from "_c/count-to";
    export default {
      name: "count_to",
      components: {
        CountTo
      },
      data() {
        return {
          text: {
            name: "本月工资",
            color: "#F4D03F"
          },
          endValue: 4000,
          decimals: 2,
        };
      }
    };
    </script>
    复制代码

    <slot v-bind="text">奖金额度:</slot>,向父级组件传递数据; slot-scope="data" 用来接收插槽传递到父组件的数据;

    新指令 v-slot

    在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slot 和 slot-scope 。

    子组件:

    <template>
      <div>
    		<!-- 向父级组件传递 textFromChild -->
        <slot :textFromChild="text" name="left">奖金额度:</slot>
        <span :id="eleId" ref="number"></span>
        <slot name="right"></slot>
      </div>
    </template>
    <script>
    import CountUp from "countup";
    export default {
      name: "CountTo",
      props: {
    	//增加 prop 
    	text:{
    		type:Object,
    		default:()=>{}
    	},
      }
    };
    </script>
    复制代码

    这样使用组件:

    <template>
      <div>
        <count-to
          :end-value="endValue"
          :decimals="decimals"
          :duration="5"
          :text="text"
        >
          <template v-slot:left="{textFromChild}">
            <span :style="{color:textFromChild.color}">{{textFromChild.name}}:</span>
          </template>
          <span slot="right"></span>
        </count-to>
      </div>
    </template>
    复制代码

    子组件传递过来的变量被放置在一个对象中,使用解构赋值的方式提取出来。

    <template v-slot:left="{textFromChild}">
      <span :style="{color:textFromChild.color}">{{textFromChild.name}}:</span>
    </template>
    复制代码

    v-slot 指令后跟一个 slot 的名字,插槽具有名字时,可简写为#

    <template #left="{textFromChild}">
      <span :style="{color:textFromChild.color}">{{textFromChild.name}}:</span>
    </template>
    复制代码

    不管有几个插槽,都把插槽内容放置在 template 中是很好的做法。

    其他杂项

    组件生成 id

    使用this_uid其他字母,可成组件内唯一的id。 count-to组件中,我们使用计算属性,设置 span 的 id。

    eleId() {
          //使用 this.uid 生成全局唯一id
          return `count_up_uid${this._uid}`;
        },
    复制代码

    在组件内部,可以通过 id 或者 class 等获取到 dom,但是不推荐这么做。可通过ref 属性,获取到DOM,更加简洁,并且可以直接通过ref 获取组件或者DOM元素。

    在下面的函数中获取DOM:

        getCount() {
          // TODO: 获取 DOM
          //使用 ref 属性获取 DOM 元素
          // console.log(this.$refs.number.innerText)
          // return this.$refs.number.innerText
    
          //使用 id 获取 DOM
          let span = document.getElementById(this.eleId);
          let currentValue = Number.parseFloat(span.innerText.split(",").join(""));
          return currentValue.toFixed(this.decimals);
        },
    复制代码

    $nextTick 函数的使用

    this.$nextTick 接收一个回调函数作为参数,参数会在 Vue 完成DOM 更新后立即调用。如果某些操作是依赖DOM更新后的,可以把这些操作放在回调函数里执行。

    • 在 created 和 mounted 阶段,如果需要操作渲染后的试图,也要使用 nextTick 方法。
    • mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted。

    Vue.$nexttick 全局的,this.$nexttick 是局部的。

    var vm = new Vue({
      el: '#example',
      data: {
        message: '123'
      }
    })
    vm.message = 'new message' // 更改数据
    vm.$el.textContent === 'new message' // false  此时DOM还没渲染
    Vue.nextTick(function () {
      vm.$el.textContent === 'new message' // true
    })
    复制代码

    Vue DOM 的更新是异步的,数据变化后,组件不会立即渲染,而是在事件队列刷新时,在下一个事件循环 tick 中渲染。

    $nexttick 返回一个 Promise,可使用 await 关键词调用。

    methods: {
      updateMessage: async function () {
        this.message = '已更新'
        console.log(this.$el.textContent) // => '未更新'
        await this.$nextTick()
        console.log(this.$el.textContent) // => '已更新'
      }
    }
    复制代码

    在父级组件中调用子组件的方法

    有时候需要再父级组件中调用子组件的方法。可以在使用组件时指定 ref ,然后使用 ref 调用。 比如调用组件的暂停方法,使得数据变化暂停。

    在组件中定义暂停方法:

    <template>
      <div>
        <slot :textFromChild="text" name="left">奖金额度:</slot>
        <span :id="eleId" ref="number" :class="countClass"></span>
        <slot name="right"></slot>
      </div>
    </template>
    <script>
    import CountUp from "countup";
    export default {
      name: "CountTo",
      data() {
        return {};
      },
      methods: {
        //TODO:  在父级组件中使用封装组件内部的方法
        // 在父级组件中调用该方法,实现暂停
        pause() {
          this.counter.pauseResume();
        }
      }
    };
    </script>
    复制代码

    在父组件中使用调用组件暂停方法。

    <template>
      <div>
        <count-to
          :end-value="endValue"
          :decimals="decimals"
          :duration="5"
          ref="countTo" 
        >
    		<!-- 指定 ref -->
          <template #left="{textFromChild}">
            <span :style="{color:textFromChild.color}">{{textFromChild.name}}:</span>
          </template>
          <span slot="right"></span>
        </count-to>
    		<button @click="pasue">暂停</button>
      </div>
    </template>
    <script>
    import CountTo from "_c/count-to";
    export default {
      name: "count_to",
      components: {
        CountTo
      },
      data() {
        return {
          endValue: 4000,
          decimals: 2,
        };
      },
      methods: {
        pasue() {
    			// 使用 refs 访问组件,然后调用器方法
          this.$refs.countTo.pause();
        }
      }
    };
    </script>
    复制代码

    样式

    组件使用样式,用三种方式:

    • 外部样式;
    • 内部样式;
    • 通过 props 传入 类名,以指定使用内部样式中的哪个类名。

    外部样式两种方法引入: 在 script 标签中引入和在 style 标签中引入。

    <template>
      <div>
        <slot :textFromChild="text" name="left">奖金额度:</slot>
    	<!-- 将 props 中的类绑定到 class 上 -->
        <span :id="eleId" ref="number" :class="countClass"></span>
        <slot name="right"></slot>
      </div>
    </template>
    <script>
    //引入样式方法一:
    // import './count-to.css'
    import CountUp from "countup";
    export default {
      name: "CountTo",
      inheritAttrs: true, //不让父作用域的属性挂载到组件的根元素上
      props: {
        /**
         * @description  自定义样式类名
         */
        className: {
          type: String,
          default: ""
        }
      }
    };
    </script>
    <style lang="css">
    /* 引入样式方法二 */
    /* @import './count-to.css' */
    /*  内部样式 */
    .count-to-number {
      color: red;
      font-size: 30px;
    }
    </style>
    复制代码

    通过 props 传递类名,实际是在父级组件中指定使用内部样式中的哪个类。

    通过 style 也可以应用样式到组件上。

    总结

    封装一个组件 props 和 data 决定了组件的核心功能,插槽用于向组件传递 html 标签,使得组件更加具有扩展性。通过事件我们可以对组件进行某些操作。改天分析一个第三方组件,好好体会一下这些概念。

    参考

    转载于:https://juejin.im/post/5d05d4f65188251bdc78d93b

    展开全文
  • Vue组件封装 ——dialog对话框组件

    千次阅读 2021-12-30 13:26:03
    2、创建components文件夹,用于存放组件,新建dialog.vue组件,可以自己取个名字 <script> export default { name: "CatDialog", }; </script> 3、在main.js中引入组件 import CatDialog from "./...

     一、基础准备工作

    1、创建一个基础的vue项目包

    2、创建components文件夹,用于存放组件,新建dialog.vue组件,可以自己取个名字

    <script>
    export default {
      name: "CatDialog",
    };
    </script>

    3、在main.js中引入组件

    import CatDialog from "./components/dialog.vue";
    Vue.component(CatDialog.name, CatDialog);

    4、App.vue中使用组件

    二、dialog组件结构搭建

           dialog对话框,整体有一个动画效果,vue的动画效果,使用transition包裹需要动画展示的元素,那么这个元素在显示/隐藏时自动添加一些类名,此例详见后续代码

            对话框分为三部分:

            1、头部:左侧为标题,使用具名插槽 title 占位,右侧为按钮/图标(关闭)

            2、主体内容,使用不具名的插槽占位

            3、底部:一般都是一些操作,使用具名插槽 footer 占位,通常内容是取消/确认按钮

            需要传入的参数:

            title:头部标题

            width:对话框宽度

            top:对话框距离顶部的距离

            visible:对话框的显示/隐藏

    页面效果:

           

     

    dialog.vue 具体代码如下,style样式太多,不逐一列出了,可根据需求自己定义: 

    <template>
      <transition name="dialog-fade">
        <!--self:事件修饰符,只有点击自己才触发,点击子元素不触发  -->
        <div class="cat-dialog__wrapper" v-show="visible" @click.self="handleClose">
          <!-- 对话框 -->
          <div class="cat-dialog" :style="{ width, marginTop: top }">
    
            <!-- 对话框顶部 标题 + 关闭按钮 -->
            <div class="cat-dialog__header">
              <slot name="title">
                <span class="cat-dialog__title">{{ title }}</span>
              </slot>
              <button class="cat-dialog__headerbtn" @click="handleClose">
                <i class="cat-icon-close"></i>
              </button>
            </div>
    
            <!-- 对话框内容 -->
            <div class="cat-dialog__body">
              <slot></slot>
            </div>
    
            <!-- 对话框底部 一般都是一些操作,没有传入footer插槽就不显示v-if -->
            <div class="cat-dialog__footer" v-if="$slots.footer">
              <slot name="footer"></slot>
            </div>
          </div>
        </div>
      </transition>
    </template>
    
    <script>
    export default {
      name: "CatDialog",
      props: {
        title: {
          type: String,
          default: "提示",
        },
        // 弹框宽度
        width: {
          type: String,
          default: "30%",
        },
        // 弹框距离顶部距离
        top: {
          type: String,
          default: "15vh",
        },
        visible: {
          type: Boolean,
          default: false,
        },
      },
      methods: {
        handleClose() {
          //visible是父组件传过来的props,子组件不能直接修改,需要子传父
          this.$emit("update:visible", false);
        },
      },
    };
    </script>

     transition动画代码:

    <style lang="scss">
    // 进入动画
    .dialog-fade-enter-active {
      animation: dialog-fade-in 0.4s;
    }
    // 离开动画
    .dialog-fade-leave-active {
      animation: dialog-fade-out 0.4s;
    }
    
    @keyframes dialog-fade-in {
      0% {
        transform: translate3d(0, -20px, 0);
        opacity: 0;
      }
      100% {
        transform: translate3d(0, 0, 0);
        opacity: 1;
      }
    }
    
    @keyframes dialog-fade-out {
      0% {
        transform: translate3d(0, 0, 0);
        opacity: 1;
      }
      100% {
        transform: translate3d(0, -20px, 0);
        opacity: 0;
      }
    }
    </style>

    App.vue使用dialog组件:

    <template>
      <div>
        <Cat-button type="primary" @click="visible = true">点击弹出</Cat-button>
        <!-- 
          sync:事件修饰符,是一个语法糖写法,实现子组件修改父组件传入的props
          父:visible.sync="visible"
          子:this.$emit("update:visible", false) 
        -->
        <Cat-dialog width="25%" top="100px" :visible.sync="visible">
          <template v-slot:title>
            <h3>标题</h3>
          </template>
          <template>
            <p>主体内容,随便写点什么...</p>
            <input type="text" placeholder="请输入信息" />
          </template>
          <template v-slot:footer>
            <Cat-button @click="visible = false">取消</Cat-button>
            <Cat-button type="primary" @click="visible = false">确定</Cat-button>
          </template>
        </Cat-dialog>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          visible: false,
        };
      },
    };
    </script>
    
    <style lang="scss" scoped>
    .cat-button {
      margin-right: 10px !important;
    }
    </style>

    展开全文
  • vue封装自定义组件

    2022-02-14 07:50:47
    说一下组件封装???? 你在项目中是如何封装组件的????? … 以上问题是面试官,最常问到的问题?那么你应该如何回答呢? 1 答: 我用vue开发的所有项目,都是采用组件化的思想开发的。一般我在搭建项目的...
    1. 你封装过组件吗??
    2. 说一下组件封装????
    3. 你在项目中是如何封装组件的?????
    以上问题是面试官,最常问到的问题?那么你应该如何回答呢?
    1
    

    答: 我用vue开发的所有项目,都是采用组件化的思想开发的。一般我在搭建项目的时候,会创建一个views目录和一个commen目录和一个feature目录,views目录中放页面级的组件,commen中放公共组件(如:head(公共头组件),foot(公共底部组件)等),feature目录内放功能组件(如:swiper(轮播功能组件),tabbar(切换功能组件)、list(上拉加载更多功能组件))

    首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低、难维护、复用性低等问题。

    使用组件的好处

    • 组件是可以复用性的
    • 易于维护
    • 封装性,易于使用
    • 大型项目中降低组件之间重复

    全局组件

    引用全局组件my-header

    <body>
    <div id="main1">
    <!-- 正常模板解析的时候,不会将自定义标签解读出来,而是将自己定义组件里的参数解读出来,也就是下面template中的h1标签 -->
    <my-header></my-header>
    </div>
    <div id="main2">
    <!-- 自定义组件具有复用性 -->
    <my-header></my-header>
    </div>
    <div id="main3">
    <my-header></my-header>
    </div>
    </body> 
    12345678910111213
    

    注册全局组件my-header

    //全局组件:在任何地方,任何方式,任何地点都可以使用的标签组件
    vue.component("my-header", {
    // h1标签是我们在自定义主键里面写的参数的标签
    template: '<h1>标题{{msg}}</h1>',
    data(){
    return {msg:100}
    }
    })
    new Vue({
    el: '#main'
    });
    new Vue({
    el: '#main2'
    });
    new Vue({
    el: '#main3'
    });
    1234567891011121314151617
    

    局部组件

    局部组件:只是在我们规定的范围之内才会生效。

     <body>
    <div id="main">
    <my-header></my-header>
    <my-header></my-header>
    </div>
    </body>
    new Vue({
    el: '#main',
    components: {
    "my-header": {
    template: '<h1>标题{{vue}}</h1>'
    }
    }
    })
    1234567891011121314
    

    注意:

    当使用 kebab-case(中划线命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如<my-component-name>

    当使用 PascalCase (驼峰式命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说
    <my-component-name><MyComponentName> 都是可接受的。注意,尽管如此,直接在 DOM(即非字符串的模板) 中使用时只有kebab-case 是有效的

    //中划线 使用语法: <my-component-name>`
    Vue.component('my-component-name', { /* ... */ })
    //驼峰  使用语法:<my-component-name> `和` <MyComponentName>`都可以
    Vue.component('MyComponentName', { /* ... */ })
    1234
    

    脚手架vue-cli中的组件

    我们一般通过脚手架vue-cli开发项目,每个 .vue单文件就是一个组件。在父组件中使用import 导入一个子组件,并在components中注册子组件,子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用$emit方法触发自定义事件传递参数。
    父传子(props)
    prop 的定义应该尽量详细,至少需要指定其类型

    <!-- 父组件 -->
    <template>
    <div>
    <my-child :parentMessage="parentMessage"></my-child>
    </div>
    </template>
    
    <script>
    import MyChild from '@components/common/MyChild'
    
    export default {
    components: {
      MyChild
    },
    data() {
      return {
          parentMessage: "我是来自父组件的消息"
      }
    }
    }
    </script>
    123456789101112131415161718192021
    <!-- 子组件 -->
    <template>
    <div>
    <span>{{ parentMessage }}</span>
    </div>
    </template>
    
    <script>
    export default {
    props: {
      parentMessage: {
          type: String,
          default: '默认显示的信息'
          // require: true // 必填
      }
    }
    }
    </script>
    123456789101112131415161718
    

    通过 $on 传递父组件方法

    通过$on传递父组件方法是组件通信中常用的方法传递方式。它可以与通过props传递方法达到相同的效果。相比于props传递function,它更加的直观和显示的表现出了调用关系

    <!-- 父组件 -->
    <template>
    <div>
    <my-child @childEvent="parentMethod"></my-child>
    </div>
    </template>
    
    <script>
    import MyChild from '@components/common/MyChild'
    
    export default {
    components: {
      MyChild,
    },
    data() {
      return {
          parentMessage: '我是来自父组件的消息',
      }
    },
    methods: {
      parentMethod() {
          alert(this.parentMessage)
      }
    }
    }
    </script>
    1234567891011121314151617181920212223242526
    <!-- 子组件 -->
    <template>
    <div>
    <h3>子组件</h3>
    </div>
    </template>
    
    <script>
    export default{
    mounted() {
      this.$emit('childEvent')
    }
    }
    </script>
    1234567891011121314
    

    $parent获取父组件然后使用父组件中的数据(不推荐)

    准确来说这种方式并不属于数据的传递而是一种主动的查找。父组件并没有主动的传递数据给子组件,而是子组件通过与父组件的关联关系,获取了父组件的数据。
    该方法虽然能实现获取父组件中的数据但是不推荐这种方式,因为vue提倡单向数据流,只有父组件交给子组件的数据子组件才有使用的权限,不允许子组件私自获取父组件的数据进行使用。在父与子的关系中子应当是处于一种被动关系

    // 此处的this为子组件实例
    this.$parent
    12
    

    通过$emit传递父组件数据 (推荐)
    与父组件到子组件通讯中的$on配套使用,可以向父组件中触发的方法传递参数供父组件使用

    <!-- 父组件 -->
    <template>
    <div>
    <my-child @childEvent="parentMethod"></my-child>
    </div>
    </template>
    
    <script>
    import MyChild from '@components/common/MyChild'
    
    export default {
    components: {
      MyChild
    },
    data() {
      return {
          parentMessage: '我是来自父组件的消息'
      }
    },
    methods: {
      parentMethod({ name, age }) {
          console.log(this.parentMessage, name, age)
      }
    }
    }
    </script>
    1234567891011121314151617181920212223242526
    <!-- 子组件 -->
    <template>
    <div>
    <h3>子组件</h3>
    </div>
    </template>
    
    <script>
    export default {
    mounted() {
      this.$emit('childEvent', { name: 'zhangsan', age:  10 })
    }
    }
    </script>
    1234567891011121314
    

    refs获取

    可以通过在子组件添加ref属性,然后可以通过ref属性名称获取到子组件的实例。准确来说这种方式和this.$parent
    一样并不属于数据的传递而是一种主动的查找。

    尽量避免使用这种方式。因为在父子组件通信的过程中。父组件是处于高位是拥有控制权,而子组件在多数情况下应该为纯视图组件,只负责视图的展示和自身视图的逻辑操作。对外交互的权利应该由父组件来控制。所以应当由父组件传递视图数据给子组件,子组件负责展示。而子组件的对外交互通过$emit触发父组件中相应的方法,再由父组件处理相应逻辑

    <!-- 父组件 -->
    <template>
    <div>
    <my-child ref="child"></my-child>
    </div>
    </template>
    
    <script>
    import MyChild from '@components/common/MyChild'
    
    export default {
    components: {
      MyChild
    },
    mounted() {
      console.log(this.$refs['child'].getData());
    }
    }
    </script>
    12345678910111213141516171819
    <!-- 子组件 -->
    <script>
    export default {
    methods: {
      getData() {
          // do something...
      }
    }
    }
    </script>
    12345678910
    

    组件样式修改 样式穿透

    css父组件修改子组件中的样式
    如果你希望scoped样式中的一个选择器能够作用得“更深”,例如影响子组件,你可以使用 >>> 操作符:

    <style scoped>
    .a >>> .b { /* ... */ }
    </style>
    123
    

    上述代码将会编译成:

    .a[data-v-f3f3eg9] .b { /* ... */ }
    1
    

    sass中语法
    有些像sass之类的预处理器无法正确解析 >>>。这种情况下你可以使用 /deep/操作符取而代之——这是一个>>>的别名,同样可以正常工作

    <style scoped>
    .a /deep/ .b { /* ... */ }
    </style>
    
    展开全文
  • Vue 组件封装之 Tab 切换

    千次阅读 多人点赞 2019-07-25 17:22:27
    实战 Vue 第5天:封装一个tab切换组件前言使用现存组件面临的问题封装 tab 组件的思路封装 tab 组件代码总结 前言 以上 tab 切换功能在前端开发中司空见惯。各种现存的组件也随手拈来。在 Vue 中,有配套的 element-...
  • 封装公共组件——模块标题-包括点击事件、背景图片、插槽、下方内容区 1、效果 2、封装的代码 src/components/header/titleHeader.vue <script lang="ts" setup> import icon from '@/assets/yfdd-bs/right/...
  • 使用单文件组件封装model的模板、逻辑和样式,之后就可以在页面中调用此通用组件。 需求 model有两个使用场景: 1、备注/修改密码(简易): 在屏幕中垂直居中 2、添加/修改/展示信息(复杂): 距离屏幕顶部200px,...
  • 1.首先,将自己想要封装组件创建好。(使用插槽、组件传参来控制这个弹窗的显示和隐藏,以及弹窗的内容) <template> <div id="qf-modal" v-if="visible"> <!-- 封装一个弹窗的组件 --> &...
  • vue封装自定义组件

    2022-02-11 20:14:33
    说一下组件封装???? 你在项目中是如何封装组件的????? … 以上问题是面试官,最常问到的问题?那么你应该如何回答呢? 答: 我用vue开发的所有项目,都是采用组件化的思想开发的。一般我在搭建项目的时候...
  • vue 组件修改网页标题

    2020-09-08 14:16:55
    import VueWechatTitle from 'vue-wechat-title' Vue.use(VueWechatTitle) 2、在App.vue里加上 <router-view v-wechat-title='$route.meta.title'></router-view> 3、在跳转路由index.js定义标题 ...
  • vue接触的多了之后,你可能也会到自己封装组件的程度,试想每个页面的功能级模块全部拆分成组件,然后后续请求后台数据传入进去或者自己模拟数据,是多么方便的一件事情。每当我们需要修改的时候,只需维护那一个...
  • 目录vue封装一个 弹窗组件 dialog使用dialog组件 Home.vue vue封装一个 弹窗组件 dialog 实现效果: 点击按钮,弹窗显示出来 点击关闭 || 取消 || 确定 弹窗隐藏 弹出时候带着遮罩层以及过渡动画效果 关闭...
  • 使用Vue封装图表组件

    2021-07-28 15:48:21
    关于vue 的学习: https://cn.vuejs.org/v2/guide/ 关于弹性布局: https://www.runoob.com/w3cnote/flex-grammar.html 关于echarts使用: https://www.runoob.com/echarts/echarts-tutorial.html Vue-Cli创建...
  • vue 封装Form表单组件

    千次阅读 2020-07-07 20:17:09
    回显表单信息 + 验证表单 + 提交表单信息,而表单的类型也有很多(复选框,单选框,下拉框,输入框,文本框等等等)如果多个页面都有表单验证的交互且表单的内容不一样,难道我们就要去写多个表单组件吗???那该...
  • vue项目中使用echarts, 1 我们要在项目中安装 echarts 依赖包 命令 : npm install echarts -S 2 引入echarts,可全局引入和 按需引入 全局引入: 在main.js 文件中引入 // 引入echarts --- 在 main.js 中 ...
  • vue 封装公共组件

    千次阅读 2020-10-26 17:38:10
    最近在写一个后台管理系统,由于多个页面都需要展示同一个内容的数据,那么咱就来封装一下。 1.首先第一种简单的父传子封装 先看效果 项目中多个页面需要展示相册,就简单封装下 1.父组件引入子组件并注册 import ...
  • Vue 弹窗 组件 封装

    千次阅读 2019-08-09 10:00:03
    近期刚入职一家公司,暂无任务安排老大要求我自己封装一个弹窗组件 里面能渲染表格 ,我思忖良久。照着网上大佬的,代码自己敲了一个弹框的封装,至于弹框上渲染表格(这里的表格是饿了吗UI里面的table组件 博主还没...
  • vue 封装组件--可复用

    2021-04-21 16:26:49
    本次封装头部标题区块。 目标样式 创建目录 在src / components / 创建 headTips 文件 封装组件 html – headTips/index.vue <template> <div class="headTips"> <van-nav-bar :title="text" ...
  • Tipes: 封装弹框组件使用了Teleport,避免了组件嵌套可能导致的定位层级的隐患,还使用了defineProps,defineEmits,插槽增加了组件的拓展性。 前言: 之前一直没有自己去封装过一个弹框组件,但是觉得一个小小的...
  • 无论浏览器窗口或页面元素如何变化,容器的宽度和高度永远保持固定的比例,如16:9。 应用场景: 如大屏自适应,内部元素对容器宽高比依赖很高的复杂模块布局等。 纯CSS实现固定宽高比的容器 原理: padding-top的...
  • 新建 editor.vue 文件 写入如下内容 <template> <div class="editor"> <!--动态绑定id, 否则不能在一个页面上使用多个--> <div :id="uniqueId"></div> </div> </...
  • vue组件化开发

    千次阅读 多人点赞 2020-09-16 15:41:11
    说一下组件封装? 你在项目中是如何封装组件的? … 以上问题是面试官,最常问到的问题?那么你应该如何回答呢? 答:我用vue开发的所有项目,都是采用组件化的思想开发的。一般我在搭建项目的时候,会创建一个...
  • vue封装一个wangeditor组件

    千次阅读 2020-07-24 23:26:07
    Vue封装一个富文本组件 以后使用就方遍了 wangeditor文档 第一步安装 npm install wangeditor --save 第二步封装组件 在@/components/Editor/WangEditor.vue <template> <div :class="prefixCls"> ...
  • Vue 组件封装之 Result 结果页

    千次阅读 2020-10-19 09:24:19
    Vue 组件封装之 Result 结果页 一、Result 结果页 二、使用案例 三、API 使用指南 四、源代码 一、Result 结果页 组件说明: 实现 Result 结果页。 效果展示: 实现的功能: 提交或者操作完成之后,进入一个成功或者...
  • Vue3 +ElementPlus 表单组件封装 在系统中,表单作为用户与后端交互的重要传递组件使用频率极高,故对其进行封装是必然的,也是一个编写规范代码的前端程序员必须做的一件事。 在Vue3中封装组件时,能感受到与Vue2...
  • https://blog.csdn.net/weixin_42321819/article/details/116521572?utm_term=vue%E5%8A%A8%E6%80%81%E8%AE%BE%E7%BD%AE%E9%A1%B5%E9%9D%A2%E6%A0%87%E9%A2%98&utm_medium=distribute.pc_aggpage_search_result....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,282
精华内容 2,112
关键字:

vue组件封装页面标题