如何获取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 页面输出值
Nov 30 Javascript
jsp+javascript打造级连菜单的实例代码
Jun 14 Javascript
解析jQuery的三种bind/One/Live事件绑定使用方法
Dec 30 Javascript
JavaScript中window.showModalDialog()用法详解
Dec 18 Javascript
javascript实现给定半径求出圆的面积
Jun 26 Javascript
Vue.use源码分析
Apr 22 Javascript
详解webpack和webpack-simple中如何引入css文件
Jun 28 Javascript
基于node.js的fs核心模块读写文件操作(实例讲解)
Sep 10 Javascript
使用Vue如何写一个双向数据绑定(面试常见)
Apr 20 Javascript
解决angularjs前后端分离调用接口传递中文时中文乱码的问题
Aug 13 Javascript
AngularJS中ng-options实现下拉列表的数据绑定方法
Aug 13 Javascript
详解mpvue开发微信小程序基础知识
Sep 23 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
Laravel 中获取上一篇和下一篇数据
2015/07/27 PHP
Zend Framework教程之Zend_Db_Table_Row用法实例分析
2016/03/21 PHP
PHP微信开发之查询微信精选文章
2016/06/23 PHP
js GridView 实现自动计算操作代码
2009/03/25 Javascript
40个新鲜出炉的jQuery 插件和免费教程[上]
2012/07/24 Javascript
2014 HTML5/CSS3热门动画特效TOP10
2014/12/07 Javascript
JS解析XML文件和XML字符串详解
2015/04/17 Javascript
js实现select跳转菜单新窗口效果代码分享(超简单)
2015/08/21 Javascript
node.js回调函数之阻塞调用与非阻塞调用
2015/11/13 Javascript
java必学必会之static关键字
2015/12/03 Javascript
快速使用Bootstrap搭建传送带
2016/05/06 Javascript
老生常谈onBlur事件与onfocus事件(js)
2016/07/09 Javascript
Javascript之面向对象--接口
2016/12/02 Javascript
轻松学习Javascript闭包
2017/03/01 Javascript
js实现简单的二级联动效果
2017/03/09 Javascript
jQuery获取table表中的td标签(实例讲解)
2017/07/28 jQuery
React Native仿美团下拉菜单的实例代码
2017/08/08 Javascript
深入理解Vue 的条件渲染和列表渲染
2017/09/01 Javascript
JavaScript 浏览器对象模型BOM原理与常见用法实例分析
2019/12/16 Javascript
解决node终端下运行js文件不支持ES6语法
2020/04/04 Javascript
[02:31]2014DOTA2国际邀请赛2009专访:干爹表现出乎意料 看好DK杀回决赛
2014/07/20 DOTA
[56:35]DOTA2上海特级锦标赛主赛事日 - 5 总决赛Liquid VS Secret第一局
2016/03/06 DOTA
使用Python的判断语句模拟三目运算
2015/04/24 Python
深入理解python函数递归和生成器
2016/06/06 Python
windows系统下Python环境的搭建(Aptana Studio)
2017/03/06 Python
Python多进程方式抓取基金网站内容的方法分析
2019/06/03 Python
python绘制无向图度分布曲线示例
2019/11/22 Python
Python 模拟生成动态产生验证码图片的方法
2020/02/01 Python
解决Python3.7.0 SSL低版本导致Pip无法使用问题
2020/09/03 Python
加拿大高尔夫超市:Golf Town
2018/01/12 全球购物
结构工程研究生求职信
2013/10/13 职场文书
大学生自我鉴定
2013/12/16 职场文书
会议活动邀请函
2014/01/27 职场文书
大学运动会通讯稿
2014/01/28 职场文书
计算机学生的自我评价分享
2014/02/18 职场文书
Windows安装Anaconda3的方法及使用过程详解
2021/06/11 Python