最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 【Vue-Element-Admin 分析】- 04 图标组件是怎么工作的?

    正文概述 掘金(初心Yearth)   2021-01-21   367

    前文链接

    • 【Vue-Element-Admin 分析】- 01 Mock 是怎么实现的?
    • 【Vue-Element-Admin 分析】- 02 网络请求的是怎么封装的?
    • 【Vue-Element-Admin 分析】- 03 权限管理是怎么实现的?

    分析

    图标组件的线索是非常多的,这里从 main.js 入手,可以看到引入了一个当前目录下的 icons

    【Vue-Element-Admin 分析】- 04 图标组件是怎么工作的?

    直接跟过去即可,代码非常简单:

    import Vue from 'vue'
    import SvgIcon from '@/components/SvgIcon' // svg component
    
    // register globally
    Vue.component('svg-icon', SvgIcon)
    
    const req = require.context('./svg', false, /\.svg$/)
    const requireAll = requireContext =>
      requireContext.keys().forEach(requireContext)
    requireAll(req)
    

    前面的部分一眼就能明白,注册了一个 vue 的全局组件。而后面的部分设计到 webpack api,我们可以看一下:依赖管理,再结合当前目录文件:

    【Vue-Element-Admin 分析】- 04 图标组件是怎么工作的?

    就能得知这段代码的功能是导入 ./svg 下的所有图标文件。

    紧接着,跳转到 SvgIcon 看看它又是如何工作的:

    <template>
      <div
        v-if="isExternal"
        :style="styleExternalIcon"
        class="svg-external-icon svg-icon"
        v-on="$listeners"
      />
      <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
        <use :xlink:href="iconName" />
      </svg>
    </template>
    
    <script>
    import { isExternal } from '@/utils/validate'
    
    export default {
      name: 'SvgIcon',
      props: {
        iconClass: {
          type: String,
          required: true
        },
        className: {
          type: String,
          default: ''
        }
      },
      computed: {
        isExternal() {
          return isExternal(this.iconClass)
        },
        iconName() {
          return `#icon-${this.iconClass}`
        },
        svgClass() {
          if (this.className) {
            return 'svg-icon ' + this.className
          } else {
            return 'svg-icon'
          }
        },
        styleExternalIcon() {
          return {
            mask: `url(${this.iconClass}) no-repeat 50% 50%`,
            '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
          }
        }
      }
    }
    </script>
    
    <style scoped>
    .svg-icon {
      width: 1em;
      height: 1em;
      vertical-align: -0.15em;
      fill: currentColor;
      overflow: hidden;
    }
    
    .svg-external-icon {
      background-color: currentColor;
      mask-size: cover !important;
      display: inline-block;
    }
    </style>
    

    代码并不复杂,可以看到,它将图标分为了内部和外部两种,具体如下:

    • 它接收两个字符类型的属性,iconClassclassName
    • 通过 iconClass,它会计算出三个计算属性:
      • isExternal:用于判断是否为外部图标
      • iconName:用于指定 usexlink:href 属性
      • styleExternalIcon:设定外部图标的样式
    • 通过 className 则会计算出第四个计算属性:
      • svgClass:显然这是图标类名

    整体原理并不算特别复杂,就是利用 Vue.component 注册全局组件和 require.context 加载文件,接下来我们就简单尝试一下将其改为 vue3 版本吧。

    Vue3 版本

    组件注册

    通过文档我们可以知道,vue3 中要注册全局组件需要通过 createApp 创建的实例进行注册,所以这里直接在 main 中引入 SvgIcon 即可:

    import { createApp } from "vue";
    import App from "./App.vue";
    import SvgIcon from "@/components/SvgIcon/index.vue";
    import "./icons";
    
    export const app = createApp(App);
    
    app
      .component("svg-icon", SvgIcon)
      .mount("#app");
    

    图标组件

    这里其实变化不大,将计算属性改一下即可:

    <template>
      <div
        v-if="isExternal"
        :style="styleExternalIcon"
        class="svg-external-icon svg-icon"
        v-on="$attrs"
      />
      <svg v-else :class="svgClass" aria-hidden="true">
        <use :xlink:href="iconName" v-on="$attrs" />
      </svg>
    </template>
    
    <script lang="ts">
    import { computed, defineComponent, PropType } from "vue";
    import { isExternal as isExternalUtil } from "@/utils/validate";
    
    export default defineComponent({
      name: "index",
      props: {
        iconClass: {
          type: String as PropType<string>,
          required: true
        },
        className: {
          type: String as PropType<string>
        }
      },
      setup(props) {
        const iconName = computed(() => `#icon-${props.iconClass}`);
        const isExternal = computed(() => isExternalUtil(props.iconClass));
        const styleExternalIcon = computed(() => {
          return {
            mask: `url(${props.iconClass}) no-repeat 50% 50%`,
            "-webkit-mask": `url(${props.iconClass}) no-repeat 50% 50%`
          };
        });
        const svgClass = computed(() => {
          if (props.className) {
            return "svg-icon " + props.className;
          } else {
            return "svg-icon";
          }
        });
        return { isExternal, styleExternalIcon, iconName, svgClass };
      }
    });
    </script>
    
    <style scoped lang="scss">
    .svg-icon {
      width: 1em;
      height: 1em;
      vertical-align: -0.15em;
      fill: currentColor;
      overflow: hidden;
    }
    
    .svg-external-icon {
      background-color: currentColor;
      mask-size: cover !important;
      display: inline-block;
    }
    </style>
    

    最后的问题

    一切都按部就班完成之后运行项目会发现还是无法使用图标组件,这是为什么呢?

    究其原因是因为没有 svg 的 loader,具体可以参考官方文档。

    这里我们也可以直接把 vue-element-admin 中的配置拷贝过来:

    chainWebpack(config) {
        // it can improve the speed of the first screen, it is recommended to turn on preload
        config.plugin("preload").tap(() => [
          {
            rel: "preload",
            // to ignore runtime.js
            // https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171
            fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
            include: "initial"
          }
        ]);
    
        // when there are many pages, it will cause too many meaningless requests
        config.plugins.delete("prefetch");
    
        // set svg-sprite-loader
        config.module
          .rule("svg")
          .exclude.add(resolve("src/icons"))
          .end();
        config.module
          .rule("icons")
          .test(/\.svg$/)
          .include.add(resolve("src/icons"))
          .end()
          .use("svg-sprite-loader")
          .loader("svg-sprite-loader")
          .options({
            symbolId: "icon-[name]"
          })
          .end();
    
        config.when(process.env.NODE_ENV !== "development", config => {
          config
            .plugin("ScriptExtHtmlWebpackPlugin")
            .after("html")
            .use("script-ext-html-webpack-plugin", [
              {
                // `runtime` must same as runtimeChunk name. default is `runtime`
                inline: /runtime\..*\.js$/
              }
            ])
            .end();
          config.optimization.splitChunks({
            chunks: "all",
            cacheGroups: {
              libs: {
                name: "chunk-libs",
                test: /[\\/]node_modules[\\/]/,
                priority: 10,
                chunks: "initial" // only package third parties that are initially dependent
              },
              elementUI: {
                name: "chunk-elementUI", // split elementUI into a single package
                priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
                test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
              },
              commons: {
                name: "chunk-commons",
                test: resolve("src/components"), // can customize your rules
                minChunks: 3, //  minimum common number
                priority: 5,
                reuseExistingChunk: true
              }
            }
          });
          // https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk
          config.optimization.runtimeChunk("single");
        });
      }
    

    起源地下载网 » 【Vue-Element-Admin 分析】- 04 图标组件是怎么工作的?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元