ES6 import from root

2019-02-08 03:12发布

问题:

I'm currently playing around with React Native. I'm trying to structure my app, however it's starting to get messy with imports.

--app/
    -- /components
        -- Loading.js
    -- index.ios.js

Now, within my index.ios.js i'm able to simply do:

import Loading from './components/Loading';

However, when I start to create more components, with a deeper directory struture, it starts to get messy:

import Loading from '.../../../../components/Loading';

I understand the preferred solution would be to make private npm modules for things, but that's overkill for a small project.

You could do a global.requireRoot type solution on the browser, but how do I implement this with import?

回答1:

Had the same issue with React. So i wrote some plugin for babel which make it possible to import the modules from the root perspective - the paths are not shorter - but it's clear what you import.

So instead of:

import 'foo' from '../../../components/foo.js';

You can use:

import 'foo' from '~/components/foo.js';

Here is the Plugin (tested and with a clear README)



回答2:

If you are using Webpack you can configure it via the resolve property to resolve a your import path.

Webpack 1

resolve: {
  root: [
    path.resolve(__dirname  + '/src')
  ]
}......

Webpack 2

resolve: {
  modules: [
    path.resolve(__dirname + '/src'),
    path.resolve(__dirname + '/node_modules')
  ]
}.....

After that you can use

import configureStore from "store/configureStore";

instead of the:

import configureStore from "../../store/configureStore";

Webpack will configure your import path from the passed resolve param.

The same stuff you can do with System.js loader but with it's own config param (it's can be map or path. Check it in the System.js documentation) (if you would like to use it. It's mostly for a Angular 2 case. But I suggest: don't use standard System.js even if you are working with ng2. Webpack is much better).



回答3:

With webpack you can also make paths starting with for example ~ resolve to the root, so you can use import Loading from '~/components/Loading';:

resolve: {
  extensions: ['.js'],
  modules: [
    'node_modules', 
    path.resolve(__dirname + '/app')
  ],
  alias: {
    ['~']: path.resolve(__dirname + '/app')
  }
}

The trick is using the javascript bracket syntax to assign the property.



回答4:

In Webpack 3 the config is slightly diffrent:

import webpack from 'webpack';
import {resolve} from 'path';

...

module: {
    loaders: [
        {
            test: /\.js$/,
            use: ["babel-loader"]
        },
        {
            test: /\.scss$|\.css$/,
            use: ["style-loader", "css-loader", "sass-loader"]
        }
    ]
},
resolve: {
    extensions: [".js"],
    alias: {
        ["~"]: resolve(__dirname, "src")
    }
},


回答5:

I just checked out a React project which is more than 6 months old and for some reasons my imports no longer worked. I tried the first answer:

import 'foo' from '~/components/foo.js';

Unfortunately this did not work.

I added an .env file in the root of my project at the same level as my package.json. I added the following line to that file and this fixed my imports in my project.

NODE_PATH=src/


回答6:

If you're using Create React App you can add paths.appSrc to resolve.modules in config/webpack.config.dev.js and config/webpack.config.prod.js.

From:

resolve: {
    modules: ['node_modules', paths.appNodeModules].concat(...

To:

resolve: {
    modules: [paths.appSrc, 'node_modules', paths.appNodeModules].concat(...

Your code would then work:

import Loading from 'components/Loading';


回答7:

If you're using Create-React-App, you just need to change the environmental variable NODE_PATH to contain the root of your project.

In your config.json do the following change to set this variable before running the react-scripts commands:

"scripts": {
  "start": "cross-env NODE_PATH=. react-scripts start",
  "build": "cross-env NODE_PATH=. react-scripts build",
  "test": "cross-env NODE_PATH=. react-scripts test",
  "eject": "react-scripts eject"
},

We're using the npm library cross-env because it works on unix and windows. The syntax is cross-env var=value command.

Now instead of import module from '../../my/module' we can do import module from 'src/my/module'

Extra details on implementation

Its important to note that cross-env's scope is limited to the command it executes, so cross-env var=val command1 && command2 will only have var set during command1. Fix this if needed by doing cross-env var=val command1 && cross-env var=val command2

create-react-app gives precedence to folders in node_modules/ over whats in NODE_PATH, which is why we're setting NODE_PATH to "." instead of "./src". Using "." requires all absolute imports to start with "src/" which means there should never be a name conflict, unless you're using some node_module called src.

(Note of caution: The solution described above replaces the variable NODE_PATH. Ideally we would append to it if it already exists. NODE_PATH is a ":" or ";" separated list of paths, depending on if its unix or windows. If anyone finds a cross-platform solution to do this, I can edit my answer.)