Соответствующие версии зависимостей:
node v10.15.0npm v6.4.1yarn v1.22.10vue-cli v4.5.9@vue/compiler-sfc v3.2.26
Гитхаб:vue-source-demo
1. Предисловие (Требования)
просто хочу прочитать*.vueИсходный код файла выделен на странице, и я не хочу использовать сторонние зависимости (на самом деле не могу найти).
2. Идеи реализации
пройти черезПользовательский блок vue-loaderфункцию, получить путь к целевому файлу, а затем передатьfsПрочтите исходный код и используйте его снова@vue/compiler-coreAPIbaseParseПреобразование прочитанного содержимого вASTсинтаксическое абстрактное дерево, тоfsИз прочитанного содержимого извлеките содержимое пользовательского блока и требуемый исходный код и, наконец, повторно повесьте два вышеупомянутых содержимого на объект компонента и непосредственно прочитайте соответствующие поля компонента.
Идеально, вне работы.
3. Реализация
Теперь, когда идея очень ясна, пришло время ее реализовать.
3.1 Инициализация проекта
vue-cliСоздайте быстрый шаблон для создания проекта, вот вторая версия 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-sfc
const fs = require('fs');
const { parse } = require('@vue/compiler-sfc');
const sourceType = ['template', 'script', 'styles']
const handlerSource = (source) => {
if (!source) {
return
}
let sourceStr = ''
sourceType.forEach(typeItem => {
if (source[typeItem] && !Array.isArray(source[typeItem])) {
sourceStr += `<${typeItem}>${source[typeItem].content}</${typeItem}>\n\n`
}
if (Array.isArray(source[typeItem])) {
sourceStr += source[typeItem].map(
sourceItem => `<${sourceItem.type}>${sourceItem.content}</${sourceItem.type}>\n\n`
).join('')
}
})
return sourceStr
}
/**
* 原来用 @vue/compiler-core 里面 baseParse方法 进行 AST 转换的
* 但是遇到了一个 bug,就是在js里面用 大于> 或 小于< 号的时候 会报如下错误:
* Syntax Error: SyntaxError: Illegal tag name. Use '<' to print '<'.
* 搜寻了一番没找到具体为啥,搞得也不是很明白
* 盲猜 baseParse方法 是用于解析标签的,但是遇上了 大于小于号,由于没有成功闭合标签所以报错了。
* ... ...
* 想到另外一个依赖 @vue/compiler-sfc 也是用来解析*.vue文件的
* 于是就尝试一下,发现没有报错,就是结果有点不一样 简单处理一下就可以了
* @param source
* @param map
*/
module.exports = function (source, map) {
// 1. 获取带有 <docs /> 标签的文件完整路径
const {resourcePath} = this
// 2. 2. 读取文件内容
const file = fs.readFileSync(resourcePath).toString()
// 3. 利用
const parsed = parse(file).descriptor
// 3. 源码转换
const sourceCode = handlerSource(parsed)
const sourceCodeTitle = parsed.customBlocks[0].content
// 4. 将结果添加到 组件对象上面
this.callback(
null,
`export default function (Component) {
Component.options.__sourceCode = ${JSON.stringify(sourceCode)}
Component.options.__sourceCodeTitle = ${JSON.stringify(sourceCodeTitle)}
}`,
map
)
}
После выполнения вышеуказанных шагов не забудьте перезапустить проект. Теперь давайте посмотрим, как это работает:
эм... не плохо,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.jsimport 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. Резюме
Он в основном завершен здесь, и многие очки знаний сейчас проданы.Если что-то не так, я надеюсь, что каждый может указать на это, и я надеюсь, что все будут терпеть меня.
спасибо здесьФан ИнханИдеи предоставлены г-ном Фанг Фан.