如何获取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 相关文章推荐
jQuery 获取URL参数的插件
Mar 04 Javascript
onkeydown事件解决按回车键直接提交数据的需求
Apr 11 Javascript
深入理解JQuery keyUp和keyDown的区别
Dec 12 Javascript
JS获取客户端IP地址、MAC和主机名的7个方法汇总
Jul 21 Javascript
如何在node的express中使用socket.io
Dec 15 Javascript
jQuery中empty()方法用法实例
Jan 16 Javascript
jquery form表单获取内容以及绑定数据
Feb 24 Javascript
angular.JS实现网页禁用调试、复制和剪切
Mar 31 Javascript
详解使用create-react-app添加css modules、sasss和antd
Jul 31 Javascript
详释JavaScript执行环境与执行栈
Apr 02 Javascript
对layui中table组件工具栏的使用详解
Sep 19 Javascript
vue+canvas实现移动端手写签名
May 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中的array数组类型分析说明
2010/07/27 PHP
php绘图中显示不出图片的原因及解决
2014/03/05 PHP
详解PHP的Yii框架中日志的相关配置及使用
2015/12/08 PHP
php 提交表单 关闭layer弹窗iframe的实例讲解
2018/08/20 PHP
JavaScript 题型问答有答案参考
2010/02/17 Javascript
Javascript实现仿WebQQ界面的“浮云”兼容 IE7以上版本及FF
2011/04/27 Javascript
jquery实现手风琴效果实例代码
2013/11/15 Javascript
js实现弹窗插件功能实例代码分享
2013/12/12 Javascript
jquery 淡入淡出效果的简单实现
2014/02/07 Javascript
jQuery+css实现的蓝色水平二级导航菜单效果代码
2015/09/11 Javascript
阻止表单提交按钮多次提交的完美解决方法
2016/05/16 Javascript
快速移动鼠标触发问题及解决方法(ECharts外部调用保存为图片操作及工作流接线mouseenter和mouseleave)
2016/08/29 Javascript
jQuery插件FusionCharts实现的MSBar3D图效果示例【附demo源码】
2017/03/23 jQuery
原生js获取left值和top值的三种方法
2017/08/02 Javascript
前端图片懒加载(lazyload)的实现方法(提高用户体验)
2017/08/21 Javascript
详解javascript常用工具类的封装
2018/01/30 Javascript
layer.open弹层查看缩略图的原图,自适应大小的实例
2019/09/05 Javascript
layer.msg()去掉默认时间,实现手动关闭的方法
2019/09/12 Javascript
[01:20]辉夜杯背景故事宣传片《辉夜传说》
2015/12/25 DOTA
Python的函数嵌套的使用方法
2014/01/24 Python
Python过滤函数filter()使用自定义函数过滤序列实例
2014/08/26 Python
Python字符串处理实现单词反转
2017/06/14 Python
Python 控制终端输出文字的实例
2019/07/12 Python
利用anaconda作为python的依赖库管理方法
2019/08/13 Python
python实现根据文件格式分类
2019/10/31 Python
pytorch实现建立自己的数据集(以mnist为例)
2020/01/18 Python
Python应用实现双指数函数及拟合代码实例
2020/06/19 Python
携程英文网站:Trip.com
2017/02/07 全球购物
北美最大的参茸药食商城:德成行
2020/12/06 全球购物
学生打架检讨书大全
2014/01/23 职场文书
霸气队列口号
2014/06/18 职场文书
手术室护士个人总结
2015/02/13 职场文书
2015年学校综合治理工作总结
2015/07/20 职场文书
商业计划书之服装
2019/09/09 职场文书
gtx1650怎么样 gtx1650显卡相当于什么级别
2022/04/08 数码科技
python垃圾回收机制原理分析
2022/04/13 Python