痛点
如果我们有这个目录:
├── webpack.config.js
├── src
│ ├── view
│ │ ├── index.js
│ │── router
│ │ ├── index.js
不使用任何方式的路径别名
的话,在/view/index.js
内要引入/router/index.js
的话,就要使用../router
。当目录结构变深的时候就会出现../../../../../
,这是一个噩梦。
所以我们的目标是用@view
指代/src/view
目录,@router
指代/src/router
目录,@src
指代/src
目录。@alias
只是形式上表示这是一个别名,@
并不是必需的,可以用$,%,^
,甚至不使用,直接src
指代/src
。
在JavaScript中的解决方案
构建工具的alias
在构建工具Webpack中可以使用 resolve.alias
属性定义。
resolve:{
alias: {
'@view': path.resolve(__dirname, 'src/view'),
'@router': path.resolve(__dirname, 'src/router')
}
}
这时在/view/index.js
中就可以使用@router[/index.js]
来引入/router/index.js
了。
使用Webpack的alias属性推荐(必需)使用绝对路径,因为Webpack采用的使用时匹配替换形式的。就是说如果alias.@router
设为 ../router
的话,在使用2router
引入的时候会被替换成../router
,虽然在/view/index.js
中能正常工作,但是在/view/user/index.js
中就找不到文件了。所以坚决建议使用绝对路径。
重写require
重写require
方法,如:sexy-require
,在require
的时候用自定义的路径替换,这个一般会在Node.js中使用。下面是一个简单实现:
const alias = {
'@view' : path.resolve(__dirname, 'src/view'),
'@router': path.resolve(__dirname, 'src/router')
}
const _require = Module.prototype.require;
Module.prototype.require = function(path) {
for (let ali in alias) {
path = path.replace(ali, alias[ali]);
}
return _require.call(this, path)
}
当然这种方法只能用在require
方式引入文件,ES Module
是用不了的。
在TypeScript中的解决方案
在TypeScript中需要在编译前配置路径映射的baseUrl和paths,用于告诉编译器到哪里去查找模块。但是编译过程中并不会将路径替换成目标路径,就是.ts
文件中的@router
在生成的.js
文件中仍然是@router
。这就要我们编译后的文件再设置一次路径别名了。如果用Webpack打包的当然可以使用构建工具的alias
完成别名。如果没有用Webpack的也可以重写require
方法,这里记得要将tsconfig.json
里面"module"
设为"commonjs"
,因为我们只能重写require
方法。
注意:如果.ts
文件存放在src
目录,编译后.js
文件存放在dist
目录,记得.ts
文件中的路径映射是要用以src
目录为基础的,而对.js
文件得路径别名要以dist
目录为基础
// tsconfig.json
// baseUrl 是源代码目录
{
"compilerOptions": {
"target": "ES2017",
"module": "commonjs",
"outDir": "dist",
"baseUrl": "src",
"paths": {
"$*": ["*"]
}
},
"include": ["src/**/*"],
"exclude": ["dist"]
}
// package.json
// path.$* 的路径都是以tsconfig.json 中 outDir 为基础的
{
"name": "alias",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node ./dist/router"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/node": "^8.0.47",
"sexy-require": "^0.1.0"
},
"path": {
"$router": "/dist/router",
"$view": "/dist/view"
}
}
// src/router/index.ts
require("sexy-require");
const view = require("$view");
console.log(view);
export = "router/index";
// src/view/index.ts
export = "view/index";