自定義 loader 讀取 *.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
實現一個。
專案跑起來是下面這個樣子的,這裡大家應該都會的,就不多贅述了。
3.2 自定義塊
這裡參考 vue-loader 官網的例子,非常的簡單。不懂的同學,可以去官網檢視。
- 建立
loader
檔案plugins/docs-loader.js
module.exports = function (source, map) {
this.callback(
null,
`export default function (Component) {
Component.options.__docs = ${
JSON.stringify(source)
}
}`,
map
)
}
- 建立
vue.config.js
配置規則使用上面定義好的loader
const docsLoader = require.resolve('./plugins/docs-loader.js')
module.exports = {
configureWebpack: {
module: {
rules: [
{
resourceQuery: /blockType=docs/,
loader: docsLoader
}
]
}
}
}
注:修改了配置相關檔案需要重跑一下專案
- 使用
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>
效果:
將 Demo
元件在控制檯輸出效果會更明顯一點:
3.4 獲取檔案路徑並顯示內容
在獲取檔案的路徑的時候,瞎澤騰了好久(此處省略好多個字),結果 webpack 的英文官網是有提到。於是就去列印一下 loader
的 this
,真的什麼都有,早知道早點列印出來看了,害!!! 留下了沒技術的眼淚。
現在已經拿到目標檔案的完整路徑了,開始搞事情!給我們自定義的 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
)
}
完成以上步驟,記得重跑專案。現在我們來看看效果如何:
em… 不錯,Demo
元件該有的都有了。再用 pre
標籤顯示出來看:
<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>
效果:
程式碼高亮了,是喜歡的顏色。亮是亮起來了,但是寫得是一次性程式碼,不大符合乾飯人的要求,是不是可以封裝一個公共元件專門來看元件的效果和原始碼的呢!
3.6 元件封裝
封裝元件之前需要構思一下這個元件應該長什麼樣呢?帶著樣的一個疑問,去瀏覽了各個優秀輪子的文件頁面,畫出了下面的設計圖:
開始全域性元件封裝:
-
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>
-
src/components/component-source-demo/index.js
import ComponentSourceDemo from './src/index' ComponentSourceDemo.install = (Vue) => Vue.component(ComponentSourceDemo.name, ComponentSourceDemo) export default ComponentSourceDemo
使用:
-
src/mian.js
全域性註冊元件 -
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>
程式碼非常的清爽,舒服!!! 效果也非常的棒,甲方很滿意。
感覺還是有點美中不足,如果有很多個需要展示的元件呢。那豈不是要寫很多的重複程式碼,作為優秀的乾飯人是不允許這種情況出現的,程式碼還需再優化一下。
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>
現在只需往 components/demo
新增的新的元件,我們只需重新整理一下webpack
就會幫我們自動讀取元件了。
4. 總結
到這裡基本完工了,很多的知識點都是現學現賣的,如果哪裡講的不對希望大家指出,哪裡講得不好希望大家多多包涵。
在這裡需要感謝 方應杭 方方老師提供的思路。
相關文章
- 【Spring原始碼分析】配置檔案讀取流程Spring原始碼
- 如何獲取 vue 單檔案自身原始碼路徑Vue原始碼
- vue-markdown-loader原始碼解析Vue原始碼
- SpringBoot讀取自定義配置檔案Spring Boot
- golang 使用 viper 讀取自定義配置檔案Golang
- Webpack Loader原始碼導讀之css-loaderWeb原始碼CSS
- Webpack Loader原始碼導讀之less-loaderWeb原始碼
- Webpack Loader原始碼導讀之babel-loaderWeb原始碼Babel
- Vue原始碼探究-原始碼檔案組織Vue原始碼
- Loader 知識梳理(3) 自定義Loader
- 直播系統原始碼,讀取多行文字、讀取檔案分割多行文字原始碼
- vite vue-cli 讀取檔案原始內容 使用base64內容的檔案ViteVue
- app直播原始碼,vue 自定義指令過濾特殊字元APP原始碼Vue字元
- 直播商城原始碼,vue 自定義指令過濾特殊字元原始碼Vue字元
- Vue原始碼閱讀 – 檔案結構與執行機制Vue原始碼
- Vue原始碼閱讀 - 檔案結構與執行機制Vue原始碼
- 在vue裡,下載自定義內容的檔案Vue
- vue 阿里雲oss下載檔案,自定義命名Vue阿里
- Go 專案配置檔案的定義和讀取Go
- springboot讀取自定義配置檔案節點Spring Boot
- 【Spring原始碼分析】.properties檔案讀取及佔位符${...}替換原始碼解析Spring原始碼
- 直播軟體原始碼,vue 自定義指令過濾特殊字元原始碼Vue字元
- 【PHP】CI框架原始碼分析核心檔案之Loader.phpPHP框架原始碼
- php 自定義配置檔案PHP
- vue-loader 原始碼解析系列之 selectorVue原始碼
- 自定義Vue-cli專案模板Vue
- Mybatis原始碼分析(七)自定義快取、分頁的實現MyBatis原始碼快取
- 成品直播原始碼,如何在開發時自定義快取策略原始碼快取
- Java 讀取檔案Java
- tiff檔案讀取
- 任意檔案讀取
- DesignBuilder自定義材料檔案UI
- vue原始碼分析系列之入口檔案分析Vue原始碼
- vue-loader 原始碼解析系列之 整體分析Vue原始碼
- 【原始碼解讀(二)】EFCORE原始碼解讀之查詢都做了什麼以及如何自定義批次插入原始碼
- Vue原始碼解讀一Vue原始碼
- 用Groovy讀取本地檔案的程式碼
- javascript讀取xml檔案程式碼例項JavaScriptXML