最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 自定义 loader 读取 *.vue 文件源码

    正文概述 掘金(阿彪理发店)   2020-12-11   493

    相关依赖版本:

    • node v10.15.0
    • npm v6.4.1
    • yarn v1.22.10
    • vue-cli v4.5.9
    • @vue/compiler v3.0.4

    GitHub:vue-source-demo

    1. 前言(需求)

    就是想读取 *.vue 文件的源码并高亮展示到页面上,又不想用第三方的依赖(其实是找不到)。

    2. 实现思路

    通过 vue-loader 自定义块 功能,获取目标文件的文件路径,然后通过 fs 读取源码,再用 @vue/compiler-core 的 API baseParse将读取到的内容转换成 AST 语法抽象树,然后将 fs 读取的内容中 抽离出 自定义块内容 和 需要的源码,最后再将以上两个内容重新挂到组件对象上,直接读取组件相应的字段就可以。

    完美,关机,下班。

    3. 实现

    现在思路已经非常的清晰,时候实现它了。

    3.1 项目初始化

    vue-cli 创建快速模板搭建项目,这里用的是 2版本的 vue,后面再用 vite + vue3 实现一个。

    自定义 loader 读取 *.vue 文件源码

    项目跑起来是下面这个样子的,这里大家应该都会的,就不多赘述了。

    自定义 loader 读取 *.vue 文件源码

    3.2 自定义块

    这里参考 vue-loader 官网的例子,非常的简单。不懂的同学,可以去官网查看。

    1. 创建loader文件 plugins/docs-loader.js
    module.exports = function (source, map) {
        this.callback(
            null,
            `export default function (Component) {
                Component.options.__docs = ${
                    JSON.stringify(source)
                }
            }`,
            map
        )
    }
    
    1. 创建 vue.config.js 配置规则使用上面定义好的 loader
    const docsLoader = require.resolve('./plugins/docs-loader.js')
    
    module.exports = {
        configureWebpack: {
            module: {
                rules: [
                    {
                        resourceQuery: /blockType=docs/,
                        loader: docsLoader
                    }
                ]
            }
        }
    }
    
    

    ​ 注:修改了配置相关文件需要重跑一下项目

    1. 使用

    src/components/demo.vue

    <docs>
        我是ComponentB docs自定义快 内容
    </docs>
    
    <template>
        <div>
            ComponentB 组件
        </div>
    </template>
    
    <script>
        export default {
            name: "ComponentB"
        }
    </script>
    
    <style scoped>
    
    </style>
    

    src/App.vue

    <template>
        <div id="app">
            <demo/>
            <p>{{demoDocs}}</p>
        </div>
    </template>
    
    <script>
        import Demo from './components/demo'
    
        export default {
            name: 'App',
            components: {
                Demo
            },
            data () {
                return {
                    demoDocs: Demo.__docs
                }
            }
        }
    </script>
    
    

    效果:

    自定义 loader 读取 *.vue 文件源码

    Demo 组件在控制台输出效果会更明显一点:

    自定义 loader 读取 *.vue 文件源码

    3.4 获取文件路径并显示内容

    在获取文件的路径的时候,瞎泽腾了好久(此处省略好多个字),结果 webpack 的英文官网是有提到。于是就去打印一下 loaderthis ,真的什么都有,早知道早点打印出来看了,害!!! 留下了没技术的眼泪。

    自定义 loader 读取 *.vue 文件源码

    现在已经拿到目标文件的完整路径了,开始搞事情!给我们自定义的 loader 稍微加一点细节:

    搞事前需要安装一下相关依赖:

    yarn add -D @vue/compiler-core
    
    const fs = require('fs');
    const {baseParse} = require('@vue/compiler-core');
    
    module.exports = function (source, map) {
        // 1. 获取带有 <docs /> 标签的文件完整路径
        const {resourcePath} = this
        // 2. 读取文件内容
        const file = fs.readFileSync(resourcePath).toString()
        // 3. 通过 baseParse 将字符串模板转换成 AST 抽象语法树
        const parsed = baseParse(file).children.find(n => n.tag === 'docs')
        // 4. 标题
        const title = parsed.children[0].content
        // 5. 将 <docs></docs> 标签和内容抽离
        const main = file.split(parsed.loc.source).join('').trim()
        // 6. 回到并添加到 组件对象上面
        this.callback(
            null,
            `export default function (Component) {
              Component.options.__sourceCode = ${JSON.stringify(main)}
              Component.options.__sourceCodeTitle = ${JSON.stringify(title)}
            }`,
            map
        )
    }
    
    

    完成以上步骤,记得重跑项目。现在我们来看看效果如何:

    自定义 loader 读取 *.vue 文件源码

    em... 不错,Demo 组件该有的都有了。再用 pre 标签显示出来看:

    自定义 loader 读取 *.vue 文件源码

    <template>
        <div id="app">
            <demo/>
            <p>{{sourceCodeTitle}}</p>
            <pre v-text="sourceCode"></pre>
        </div>
    </template>
    
    <script>
        import Demo from './components/demo'
    
        export default {
            name: 'App',
            components: {
                Demo
            },
            data () {
                return {
                    sourceCodeTitle: Demo.__sourceCodeTitle,
                    sourceCode: Demo.__sourceCode
                }
            },
            mounted() {
                console.log('Demo', Demo)
            }
        }
    </script>
    
    

    到这里需求好像已经全部实现,很是轻松,作为一个刚毕业五个月的干饭人怎么能止步在这里呢!我决定让这平平无奇的代码高亮起来,让他变得漂漂亮亮的。

    3.5 代码高亮

    代码高亮用了一个 star 比较高的 highlightjs。

    安装:

    yarn add highlight.js
    

    使用:

    src/App.vue

    <template>
        <div id="app">
            <demo/>
            <p>{{sourceCodeTitle}}</p>
            <pre>
                <code class="language-html" ref="code" v-text="sourceCode" />
            </pre>
        </div>
    </template>
    
    <script>
        import Demo from './components/demo'
        import highlightjs from 'highlight.js'
        import 'highlight.js/styles/vs2015.css'
    
        export default {
            name: 'App',
            components: {
                Demo
            },
            data () {
                return {
                    sourceCodeTitle: Demo.__sourceCodeTitle,
                    sourceCode: Demo.__sourceCode
                }
            },
            async mounted() {
                await this.$nextTick()
                this.init()
            },
            methods: {
                init () {
                    const codeEl = this.$refs.code
                    highlightjs.highlightBlock(codeEl)
                }
            }
        }
    </script>
    
    

    效果:

    自定义 loader 读取 *.vue 文件源码

    代码高亮了,是喜欢的颜色。亮是亮起来了,但是写得是一次性代码,不大符合干饭人的要求,是不是可以封装一个公共组件专门来看组件的效果和源码的呢!

    3.6 组件封装

    封装组件之前需要构思一下这个组件应该长什么样呢?带着样的一个疑问,去浏览了各个优秀轮子的文档页面,画出了下面的设计图:

    自定义 loader 读取 *.vue 文件源码

    开始全局组件封装:

    1. src/components/component-source-demo/src/index.vue

      <template>
          <div class="component-source-demo">
              <h2 class="component-source-demo__title">{{title || component.__sourceCodeTitle}}</h2>
              <div class="component-source-demo__description">{{description}}</div>
              <div class="component-source-demo__component">
                  <component :is="component" :key="component.__sourceCodeTitle"/>
              </div>
              <div class="component-source-demo__action">
                  <button type="button" @click="handleCodeVisible('hide')" v-if="codeVisible">隐藏代码 ↑</button>
                  <button type="button" @click="handleCodeVisible('show')" v-else>查看代码 ↓</button>
              </div>
              <div class="component-source-demo__code" v-show="codeVisible">
            <pre>
              <code class="html" ref="code" v-text="component.__sourceCode"/>
            </pre>
              </div>
          </div>
      </template>
         
      <script>
          import {highlightBlock} from 'highlight.js';
          import 'highlight.js/styles/vs2015.css'
         
          export default {
              name: "component-source-demo",
              props: {
                  title: String,
                  description: String,
                  component: {
                      type: Object,
                      required: true
                  }
              },
              data() {
                  return {
                      codeVisible: true
                  }
              },
              async mounted() {
                  await this.$nextTick()
                  this.init()
              },
              methods: {
                  init () {
                      const codeEl = this.$refs.code
                      highlightBlock(codeEl)
                  },
                  handleCodeVisible(status) {
                      this.codeVisible = status === 'show'
                  }
              }
          }
      </script>
         
      <style scoped>
         
      </style>
         
      
    2. src/components/component-source-demo/index.js

      import ComponentSourceDemo from './src/index'
         
      ComponentSourceDemo.install = (Vue) => Vue.component(ComponentSourceDemo.name, ComponentSourceDemo)
         
      export default ComponentSourceDemo
         
      

    使用:

    1. src/mian.js 全局注册组件

      自定义 loader 读取 *.vue 文件源码

    2. src/App.vue

      <template>
          <div id="app">
              <component-source-demo :component="Demo"/>
          </div>
      </template>
         
      <script>
          import Demo from './components/demo'
         
          export default {
              name: 'App',
              data () {
                  return {
                      Demo
                  }
              }
          }
      </script>
         
      

      代码非常的清爽,舒服!!! 效果也非常的棒,甲方很满意。

      自定义 loader 读取 *.vue 文件源码

      感觉还是有点美中不足,如果有很多个需要展示的组件呢。那岂不是要写很多的重复代码,作为优秀的干饭人是不允许这种情况出现的,代码还需再优化一下。

    3.7 代码优化

    3.7.1 组件自动引入

    src/App.vue

    <template>
        <div id="app">
            <component-source-demo
                    v-for="item in componentList"
                    :key="item.name"
                    :component="item"
            />
        </div>
    </template>
    
    <script>
        export default {
            name: 'App',
            data () {
                return {
                    componentList: []
                }
            },
            mounted() {
                this.autoImportComponents()
            },
            methods: {
                autoImportComponents () {
                    const moduleList = require.context('./components/demo', false, /\.vue$/)
                    const requireAll = requireContext => requireContext.keys().map(requireContext)
                    let targetModuleList = requireAll(moduleList)
                    this.componentList = targetModuleList.map(module => {
                        return module.default
                    })
                }
            }
        }
    </script>
    
    

    自定义 loader 读取 *.vue 文件源码

    自定义 loader 读取 *.vue 文件源码

    现在只需往 components/demo 添加的新的组件,我们只需刷新一下webpack 就会帮我们自动读取组件了。

    4. 总结

    到这里基本完工了,很多的知识点都是现学现卖的,如果哪里讲的不对希望大家指出,哪里讲得不好希望大家多多包涵。

    在这里需要感谢 方应杭 方方老师提供的思路。


    起源地下载网 » 自定义 loader 读取 *.vue 文件源码

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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