如何获取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实现pager控件示例
Apr 09 Javascript
js创建对象的区别示例介绍
Jul 24 Javascript
javascript 广告移动特效的实现代码
Jun 25 Javascript
AngularJS实现动态编译添加到dom中的方法
Nov 04 Javascript
微信小程序中的swiper组件详解
Apr 14 Javascript
详解Webstorm 新建.vue文件支持高亮vue语法和es6语法
Oct 26 Javascript
React props和state属性的具体使用方法
Apr 12 Javascript
jQuery+Datatables实现表格批量删除功能【推荐】
Oct 24 jQuery
vue实现手机端省市区区域选择
Sep 27 Javascript
Vue组件基础用法详解
Feb 05 Javascript
解决vue bus.$emit触发第一次$on监听不到问题
Jul 28 Javascript
详解vue修改elementUI的分页组件视图没更新问题
Nov 13 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开发环境配置记录
2011/01/14 PHP
ThinkPHP中自定义错误页面和提示页面实例
2014/11/22 PHP
php+mysql实现简单的增删改查功能
2015/07/13 PHP
一个简单的js动画效果代码
2010/07/20 Javascript
jquery下动态显示jqGrid以及jqGrid的属性设置容易出现问题的解决方法
2010/10/22 Javascript
让低版本浏览器支持input的placeholder属性(js方法)
2013/04/03 Javascript
JS设置获取cookies的方法
2014/01/26 Javascript
nodejs npm包管理的配置方法及常用命令介绍
2014/06/05 NodeJs
JS+CSS实现的拖动分页效果实例
2015/05/11 Javascript
Javascript数组Array方法解读
2016/03/13 Javascript
AngularJs学习第八篇 过滤器filter创建
2016/06/08 Javascript
使用 Node.js 对文本内容分词和关键词抽取
2017/05/27 Javascript
JS实现监控微信小程序的原理
2018/06/15 Javascript
实例介绍JavaScript中多种组合继承
2019/01/20 Javascript
vue实现后台管理权限系统及顶栏三级菜单显示功能
2019/06/19 Javascript
[02:58]献给西雅图的情书_高清
2014/05/29 DOTA
[01:36]DOTA2完美大师赛趣味视频之与队友相处的十万个技巧
2017/11/19 DOTA
Django静态资源URL STATIC_ROOT的配置方法
2014/11/08 Python
Python中利用原始套接字进行网络编程的示例
2015/05/04 Python
使用Python脚本将文字转换为图片的实例分享
2015/08/29 Python
Python下实现的RSA加密/解密及签名/验证功能示例
2017/07/17 Python
Python3调用微信企业号API发送文本消息代码示例
2017/11/10 Python
python按键按住不放持续响应的实例代码
2019/07/17 Python
Python如何将图像音视频等资源文件隐藏在代码中(小技巧)
2020/02/16 Python
微信小程序之html5 canvas绘图并保存到系统相册
2019/06/20 HTML / CSS
台湾森森购物网:U-mall
2017/10/16 全球购物
Etam艾格英国官网:法国著名女装品牌
2019/04/15 全球购物
加拿大品牌鞋包连锁店:Little Burgundy
2021/02/28 全球购物
环保倡议书范文
2014/05/12 职场文书
会议室标语
2014/06/21 职场文书
街道社区活动报告
2015/02/05 职场文书
预备党员群众意见
2015/06/01 职场文书
基于Redis过期事件实现订单超时取消
2021/05/08 Redis
浅谈Python中的正则表达式
2021/06/28 Python
JavaScript高级程序设计之基本引用类型
2021/11/17 Javascript
Win11局域网共享权限在哪里设置? Win11高级共享的设置技巧
2022/04/05 数码科技