如何获取vue单文件自身源码路径


Posted in Javascript onMay 06, 2019

这个问题要从一个想法说起。

D2Admin 是一个开源的,前端中后台集成方案,原先是基于 vue-cli2,大概是向 vue-cli3 过渡时,
作者老李,想在页面右下角加个 Toggle 点击,跳到当前页面源码对应的 github 页面。

确实很实用的功能,D2Admin 的 Demo 页面太多了,想看某个页面的源码,对于不熟悉项目目录结构的新手很不友好。
这些页面统一为 .vue 组件,那么转换一下:如何获取 vue 单文件自身源码路径?

目前经历了三个方案,最终目标是把自身路径赋值到 this.$options.__source 上。目前方案 3 是最新的。

方案 1 :node + __filename

直接使用 node 中的 __filename 变量:

<template>
 <h1>{{ $options.__source }}</h1>
</template>

<script>
export default {
 mounted() {
  this.$options.__source = __filename
 }
}
</script>

因为 webpack 编译时,会把源码文件在内部转为 node 模块,.vue 文件中的 script 内容也被转换了,
其中的 __filename 在编译时被运行,直接得到当前文件自身路径。

使用这个变量还需要在 webpack 配置中启用 node.__filename:

/* vue.config.js */
module.exports = {
 // ...
 chainWebpack: config => {
  // ...
  config.node
   .set('__dirname', true) // 同理
   .set('__filename', true)
 }
};

缺点

  1. 要在每个组件里手动赋值,还不能用 mixin
  2. __filename 得到的路径在部分 .vue 文件下并不准确,路径可能还会带附带 querystring

一开始,坚强的老李用这个方式,给上百个组件手动挂上了路径,但总比手动写死每个路径要好

方案 2 :vue-loader + exposeFilename

在 loader 层面,其实 vue-loader 提供了一个 exposeFilename 选项,只要启用,
就会给每个 .vue 组件带上 this.$options.__file,上面有准确的路径。
这样只需要改 loader 配置:

/* vue.config.js */
module.exports = {
 // ...
 chainWebpack: config => {
  // ...
  config.module
   .rule('vue')
   .use('vue-loader')
    .loader('vue-loader')
    .tap(options => {
     options.exposeFilename = true
     return options
    })
 }
};

开发环境默认是开启 exposeFilename 的。

此时组件内应该直接取 this.$options.__file,内容大致为 src/foo/bar.vue。

缺点

为了安全,vue-loader 在生产环境将 __file 赋值为文件名,非路径名,详见文档

后来得知这个方法,老李就第一时间改代码,删了方案 1 中的所有附加代码,结果发现生产环境结果不一致,翻车了orz

方案 3 :loader + Custom Block

既然方案 2 不让在生产环境用,那就自己写 loader 去加上这个源码路径,这里采用了Custom Block。

这个方法是慢慢调试自定义的 loader 摸索出来的,先在 vue-loader 之前加自定义的 loader A,

去注入 Custom Block 代码,再在全局加入一个针对该 Custom Block 的 loader B。

这里将方案封装,在 chainWebpack 中调用即可:

/* vue.config.js */
const VueFilenameInjector = require('./path/to/vue-filename-injector')

module.exports = {
 chainWebpack: config => {
  VueFilenameInjector(config, {
   propName: '__source' // default
  })
 }
}

源码参考:@d2-projects/d2-advance/tools/vue-filename-injector

.
└── vue-filename-injector
  ├── README.md
  ├── index.js
  └── src
    ├── index.js
    └── lib
      ├── config.js
      ├── injector.js
      └── loader.js

vue-filename-injector/index.js:

const { blockName } = require('./lib/config.js')

// for chainWebpack
module.exports = function(config, options) {
 // 注入
 config.module
  .rule('vue')
  .use('vue-filename-injector')
  .loader(require.resolve('./lib/injector.js'))
  .options(options)
  .after('vue-loader') // 不知为何 .before() 不是预期的结果,这里就绕了一下
  .end()
 // 解析
 config.module
  .rule('')
  .resourceQuery(new RegExp(`blockType=${blockName}`))
  .use('vue-filename-injector-loader')
  .loader(require.resolve('./lib/loader.js'))
  .end()
}

vue-filename-injector/lib/config.js:

const defaultPropName = '__source'
const blockName = 'vue-filename-injector'

module.exports = {
 defaultPropName,
 blockName
}

vue-filename-injector/lib/injector.js,源码部分来自 vue-loader:

const path = require('path')
const loaderUtils = require('loader-utils')

const { blockName, defaultPropName } = require('./config.js')

module.exports = function (content /*, map, meta */) {
 const loaderContext = this

 const {
  rootContext,
  resourcePath
 } = loaderContext

 const context = rootContext || process.cwd()
 const options = loaderUtils.getOptions(loaderContext) || {}
 const rawShortFilePath = path
  .relative(context, resourcePath)
  .replace(/^(\.\.[\/\\])+/, '')

 const propName = options.propName || defaultPropName

 content += `
<${blockName}>
export default function (Component) {
 Component.options.${propName} = ${JSON.stringify(rawShortFilePath.replace(/\\/g, '/'))}
}
</${blockName}>
`
 return content
}

vue-filename-injector/lib/loader.js:

module.exports = function(source, map) {
 this.callback(null, source, map)
}

相关仓库

可进入预览页面查看效果,在右下角有 Toggle

github.com/d2-projects… (可能还在翻车中)

github.com/d2-projects…

总结

目前看来,用自定义 loader 去注入代码是最便捷的方案,不用在每个组件中手写重复的代码。

由于 vue-cli3 采用 chainWebpack,加上个人对 webpack 的理解更是不深,暂时采用方案 3。

以上所述是小编给大家介绍的如何获取vue单文件自身源码路径详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
JS 建立对象的方法
Apr 21 Javascript
js tab 选项卡
Apr 26 Javascript
关于JavaScript定义类和对象的几种方式
Nov 09 Javascript
javascript实现的DES加密示例
Oct 30 Javascript
JavaScript获取网页、浏览器、屏幕高度和宽度汇总
Dec 18 Javascript
深入学习JavaScript对象
Oct 13 Javascript
数据结构中的各种排序方法小结(JS实现)
Jul 23 Javascript
微信js-sdk分享功能接口常用逻辑封装示例
Oct 13 Javascript
JavaScript面向对象的程序设计(犯迷糊的小羊)
May 27 Javascript
JS的Ajax与后端交互数据的实例
Aug 08 Javascript
vue+vue-router转场动画的实例代码
Sep 01 Javascript
解决Vue中的生命周期beforeDestory不触发的问题
Jul 21 Javascript
详解vue-cli中使用rem,vue自适应
May 06 #Javascript
用Vue编写抽象组件的方法
May 06 #Javascript
JS解惑之Object中的key是有序的么
May 06 #Javascript
微信小程序和百度的语音识别接口详解
May 06 #Javascript
微信小程序生成海报分享朋友圈的实现方法
May 06 #Javascript
vue项目打包后上传至GitHub并实现github-pages的预览
May 06 #Javascript
Vue实现购物车的全选、单选、显示商品价格代码实例
May 06 #Javascript
You might like
PHP中对数据库操作的封装
2006/10/09 PHP
删除及到期域名的查看(抢域名必备哦)
2008/05/14 PHP
php设计模式 Visitor 访问者模式
2011/06/28 PHP
PHP中Enum(枚举)用法实例详解
2015/12/07 PHP
Smarty3配置及入门语法
2017/02/22 PHP
浅谈laravel 5.6 安装 windows上使用composer的安装过程
2019/10/18 PHP
js实现的网页颜色代码表全集
2007/07/17 Javascript
jQuery 使用手册(五)
2009/09/23 Javascript
JS判断元素为数字的奇异写法分享
2012/08/01 Javascript
代码触发js事件(click、change)示例应用
2013/12/13 Javascript
JQuery插件ajaxfileupload.js异步上传文件实例
2015/05/19 Javascript
$.extend 的一个小问题
2015/06/18 Javascript
js使用cookie记录用户名的方法
2015/11/26 Javascript
JavaScript学习笔记之创建对象
2016/03/25 Javascript
一览画面点击复选框后获取多个id值的方法
2016/05/30 Javascript
浅谈Javascript数据属性与访问器属性
2016/07/26 Javascript
BootStrap入门教程(二)之固定的内置样式
2016/09/19 Javascript
vue-cli 构建骨架屏的方法示例
2018/11/08 Javascript
js如何获取访问IP、地区、当前操作浏览器
2019/07/23 Javascript
vue自定义正在加载动画的例子
2019/11/14 Javascript
js对象属性名驼峰式转下划线的实例代码
2020/09/17 Javascript
js+for循环实现字符串自动转义的代码(把后面的字符替换前面的字符)
2020/12/24 Javascript
[48:31]DOTA2-DPC中国联赛 正赛 Dynasty vs XG BO3 第一场 2月2日
2021/03/11 DOTA
介绍Python的@property装饰器的用法
2015/04/28 Python
使用python3+xlrd解析Excel的实例
2018/05/04 Python
深入浅析Python中list的复制及深拷贝与浅拷贝
2018/09/03 Python
在python中实现强制关闭线程的示例
2019/01/22 Python
使用 Python 合并多个格式一致的 Excel 文件(推荐)
2019/12/09 Python
Sublime Text3最新激活注册码分享适用2020最新版 亲测可用
2020/11/12 Python
英国领先的亚洲旅游专家:Wendy Wu Tours
2018/01/21 全球购物
法国亚马逊官方网站:Amazon.fr
2020/12/19 全球购物
杭州-飞时达软件有限公司.net笔面试
2012/04/28 面试题
五星级酒店餐饮部总监的标准岗位职责
2014/02/17 职场文书
企业年会祝酒词
2015/08/11 职场文书
先进个人主要事迹范文
2015/11/04 职场文书
教你如何用Python实现人脸识别(含源代码)
2021/06/23 Python