因为项目需要,之前封装了一个图片点击预览的全局组件。这次我们尝试将它封装成一个插件。通过命令式的方式来达到点击预览的效果。
在封装插件之前。我们要对vue的插件机制有个基本了解。vue的插件的核心是use方法。
其实就是在Vue类上添加一个use方法,这个方法看传入第一个参数是否是函数,如果是函数则直接执行,如果是对象,就看是否有install方法,然后执行install方法,执行的时候把Vue类传入,同时把用户传入的option也传入
- Vue.use = function (plugin) {
- // 忽略已注册插件
- if (plugin.installed) {
- return
- }
-
- // 集合转数组,并去除第一个参数
- var args = toArray(arguments, 1);
-
- // 把this(即Vue)添加到数组的第一个参数中
- args.unshift(this);
-
- // 调用install方法
- if (typeof plugin.install === 'function') {
- plugin.install.apply(plugin, args);
- } else if (typeof plugin === 'function') {
- plugin.apply(null, args);
- }
-
- // 注册成功
- plugin.installed = true;
- return this;
- };
而在install方法里。vue给我们提供了几种插件注册的方式
(1) 全局minx混入 (2)全局组件注册 (3)给vue对象的原型添加该插件
也就是说。插件调用的方式我们可以选择在全局组件的方式调用,也可以选择在vue原型上调用或者在minx混入里的某个时机去调用。
说了那么多,我们开始自己的图片预览组件的开发
(1) 我们在src下新建一个plugins文件夹。新建两个文件:
prevImg.vue和index.js
- <template>
- <div class="prewImg-wrap" v-show="show">
- <div class="mask">div>
- <div class="action">
- <i class="el-icon-minus ic">i>
- <i class=" ic el-icon-plus">i>
- <i class=" ic el-icon-refresh-right">i>
- <i class=" ic el-icon-close" @click="close">i>
- div>
- <div class="img-wrap">
- <span v-if="loading" style="color:#fff">加载中...span>
- <img v-else ref="img" :src="url" alt="" class="pic"
- @load="handleImgLoad"
- @error="handleImgError">
- div>
- div>
- template>
-
- <script>
- export default {
- data(){
- return {
- show: false,
- loading:false,
- url:"https://s1.chu0.com/src/img/gif/30/30e530c90d674d26aa5afb35ab7eda84.gif"
- }
- },
- mounted(){
- this.loadImg()
- },
- watch:{
- url(val) {
- this.$nextTick(_ => {
- const $img = this.$refs.img;
- if (!$img.complete) {
- this.loading = true;
- }
- });
- }
- },
- methods:{
- close(){
- this.show=false
- },
- handleImgLoad(){
- this.loading=false
- },
- handleImgError(e){
- console.log("e-rror--",e)
- this.loading=false
- }
- }
-
- }
- script>
- <style scoped>
-
- .prewImg-wrap{
- position:fixed;
- left:0;
- top:0;
- width: 100%;
- height: 100%;;
- overflow: hidden;
- z-index:1;
- }
- .action{
- width: 200px;
- padding: 20px;
- position: absolute;
- right:10px;
- top:20px;
- color:#fff;
- z-index: 30;
- display: flex;
- justify-content: flex-end;
- }
- .ic{
- display: inline-block;
- margin-right: 10px;
- }
- .mask{
- background: rgba(0,0,0,0.6);
- position: absolute;
- top:0;
- left:0;
- z-index: 10;
- width: 100%;
- height: 100%;
- }
- .img-wrap{
- position: absolute;
- top:0;
- left:0;
- width: 100%;
- height: 100%;
- z-index:20;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .pic{
- max-height: 100%;
- max-width: 100%;
- }
-
- style>
prevImg/index.js
- import PrevView from "./prewImg.vue"
- export default {
-
- install(Vue,options){
- let PrevVueConstructor= Vue.extend(PrevView)
- let instance= new PrevVueConstructor()
- document.body.appendChild(instance.$mount().$el);
-
- let prevObj={
- show(opts={text:"",url:""}){
- console.log("instance:",instance)
- instance.show=true
- instance.text=opts.text||""
- instance.url=opts.url||""
- },
- hide(){
- instance.show=false
- instance=null
- }
- }
- if(!Vue.$prevObj){
- Vue.$prevObj=prevObj
- }
- Vue.prototype.$prevImg = prevObj
- }
-
- }
这个文件使我们这个插件的核心。首先我们通过vue.extend构造一个基于vue的构造函数,它类似于vue的实例。具备vue实例的所有属性和方法,和根vue实例不同的是,通过extend构造的子类只其data只能是一个函数。我们创建Vue实例时,都会有一个el选项,来指定实例的根节点,如果不写el选项,那组件就处于未挂载状态。Vue.extend 的作用,就是基于 Vue 构造器,创建一个‘ 子类 ',再配合$mount,就可以渲染组件,并且挂载到任意指定的节点上,比如body(这是单文件组件做不到的)
在show方法中,console.log("instance:",instance)能看到它的具体结构。也正是因为它。所以我们可以直接访问到我们prewImg组件中data定义的数据。比如show,我们就可以通过操作show变量来控制open和hide方法直接让prevImg组件展示或隐藏。
最后我们把这个prevImg对象挂载到vue的原型上。
(2)main.js中引入 prevImg文件夹下的index.js
- import Vue from 'vue'
- import App from './App.vue'
- import "./directives/composImg"
- Vue.config.productionTip = false
- import ElementUI from 'element-ui';
- import 'element-ui/lib/theme-chalk/index.css';
- import prewImg from "./plugins/prewImg"
-
- Vue.use(ElementUI);
-
- Vue.use(prewImg)
- new Vue({
- render: h => h(App),
- }).$mount('#app')
(3)具体使用
- <el-button @click="openPrev">打开预览el-button>
- <el-button @click="closePrve">关闭预览el-button>
- <script>
- export default{
-
- data(){
- return{
- restaurants: [],
- state1: '',
- url:"https://baj-dabanjiz-conf.oss-cn-hangzhou.aliyuncs.com/intelligent-design/image/20210730/middle/9bbeb6570f7b416b1bcbcc59a1b38635.jpg",
- url2:"https://fuss10.elemecdn.com/8/27/f01c15bb73e1ef3793e64e6b7bbccjpeg.jpeg"
-
- }
- },
- }
- methods:{
-
- openPrev(){
- this.$prevImg.show({url:this.url2})
- },
- closePrve(){
- this.$prevImg.hide()
- },
- }
-
- script>
-
-
