如何获取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 相关文章推荐
JavaScript快速检测浏览器对CSS3特性的支持情况
Sep 26 Javascript
jquery struts 验证唯一标识(公用方法)
Mar 27 Javascript
js 触发select onchange事件代码
Mar 20 Javascript
Javascript验证用户输入URL地址是否为空及格式是否正确
Oct 09 Javascript
JavaScript学习笔记之Cookie对象
Jan 22 Javascript
JS SetInterval 代码实现页面轮询
Aug 11 Javascript
vue-cli系列之vue-cli-service整体架构浅析
Jan 14 Javascript
新手快速入门微信小程序组件库 iView Weapp
Jun 24 Javascript
JavaScript队列结构Queue实现过程解析
Mar 07 Javascript
vue-cli3单页构建大型项目方案
Apr 07 Javascript
在vue中使用vant TreeSelect分类选择组件操作
Nov 02 Javascript
vue祖孙组件之间的数据传递案例
Dec 07 Vue.js
详解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之数据库操作详解及乱码解决!
2007/01/02 PHP
改变Apache端口等配置修改方法
2008/06/05 PHP
ThinkPHP3.1新特性之多数据库操作更加完善
2014/06/19 PHP
php开启与关闭错误提示适用于没有修改php.ini的权限
2014/10/16 PHP
PHP使用memcache缓存技术提高响应速度的方法
2014/12/26 PHP
PHP 布尔值的自增与自减的实现方法
2018/05/03 PHP
javascript indexOf函数使用说明
2008/07/03 Javascript
突发奇想的一个jquery插件
2010/11/19 Javascript
js(jQuery)获取时间的方法及常用时间类搜集
2013/10/23 Javascript
JavaScript实现显示函数调用堆栈的方法
2016/04/21 Javascript
JavaScript中闭包之浅析解读(必看篇)
2016/08/25 Javascript
浅谈javascript中的三种弹窗
2016/10/21 Javascript
JavaScript中this的用法及this在不同应用场景的作用解析
2017/04/13 Javascript
微信小程序request出现400的问题解决办法
2017/05/23 Javascript
Bootstrap table使用方法记录
2017/08/23 Javascript
JS中的Replace()传入函数时的用法详解
2017/09/11 Javascript
react中fetch之cors跨域请求的实现方法
2018/03/14 Javascript
vue实现将数据存入vuex中以及从vuex中取出数据
2019/11/08 Javascript
Vue实现圆环进度条的示例
2021/02/06 Vue.js
Python实现在线暴力破解邮箱账号密码功能示例【测试可用】
2017/09/06 Python
pandas数据处理基础之筛选指定行或者指定列的数据
2018/05/03 Python
Python3+django2.0+apache2+ubuntu14部署网站上线的方法
2018/07/07 Python
matplotlib给子图添加图例的方法
2018/08/03 Python
对Python实现累加函数的方法详解
2019/01/23 Python
Marc Jacobs彩妆官网:Marc Jacobs Beauty
2017/07/03 全球购物
西班牙电子产品购物网站:Electronicamente
2018/07/26 全球购物
JSF界面控制层技术
2013/06/17 面试题
入党综合考察材料
2014/06/02 职场文书
低碳环保演讲稿
2014/08/28 职场文书
五年级上册复习计划
2015/01/19 职场文书
2015年大学社团工作总结
2015/04/09 职场文书
2015年司法局工作总结
2015/05/22 职场文书
刑事上诉状范文
2015/05/22 职场文书
2019七夕节祝福语36句,快来收藏吧
2019/08/06 职场文书
Python基础详解之邮件处理
2021/04/28 Python
vue实现Toast组件轻提示
2022/04/10 Vue.js