vs code vue 语法提示不全_Vue造轮子必备*.vue文件源码读取并高亮展示

相关依赖版本:

  • 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 实现一个。

ff3c8ef0dde338031d8e4c877efefa19.png

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

40047d9d4ac3b29dc11112fd30f7c1b2.png

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>

效果:

48aa57321ff84ebf13d1c3fc57f7453d.png

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

c19fe01cbc162e0e62606000ab077e2f.png

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

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

e6189ac10dcd6b4e354e15d308e302f3.png

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

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

yarn add -D @vue/compiler-core
const fs = require('fs');const {baseParse} = require('@vue/compiler-core');module.exports = function (source, map) {    // 1. 获取带有  标签的文件完整路径    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. 将  标签和内容抽离    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    )}

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

12f71116145e2fbc6335346f0dabb033.png

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

6eaafbd7398ea59655d63afdc67b8abf.png

<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>

效果:

4c8e088a80fa6c88b64f7a49b0cb8f77.png

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

3.6 组件封装

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

10b9c6040388d8e3ea05262206c18f95.png

开始全局组件封装:

  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 全局注册组件

    a1e58d5c19e77b584ee80012f37a461a.png

  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>

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

    e76e2ee70281f30c2ff75738b0283631.gif

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

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>

751303e0ba079c2775f3df359d307e6c.png

4290be67b6b7cbada97530255ead387e.png

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

4. 总结

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

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值