简体   繁体   English

package.json 中节点和浏览器的不同主要入口点

[英]Different main entry point in package.json for node and browser

In isomorphic react app I have myModule which should behave differently on node and browser environments.在同构反应应用程序中,我有myModule ,它在节点和浏览器环境中的行为应该有所不同。 I would like configure this split point in package.json for myModule :我想在package.jsonmyModule配置这个分割点:

package.json

{
  "private": true,
  "name": "myModule",
  "main": "./myModule.server.js",
  "browser": "./myModule.client.js"
}

file structure

├── myModule
│   ├── myModule.client.js
│   ├── myModule.server.js
│   └── package.json
│ 
├── browser.js
└── server.js

So when I use myModule in node I should get only myModule.server.js :所以当我在node 中使用myModule ,我应该只得到myModule.server.js

server.js

import myModule from './myModule';
myModule(); // invoke myModule.server.js

On the browser side should build bundle only with myModule.client.js :浏览器端应该只使用myModule.client.js构建 bundle

browser.js

import myModule from './myModule';
myModule(); // invoke myModule.client.js

react-starter-kit uses this approach but I can't figure out where is this configuration defined. react-starter-kit使用这种方法,但我不知道这个配置在哪里定义。


Motivation动机

  1. package.json is good semantic point to do this kind of splitting. package.json是进行这种拆分的良好语义点。
  2. Client side bundle only contain myModule.client.js .客户端包只包含myModule.client.js

Known solution - not an answer for me已知的解决方案 - 不是我的答案

You can have this kind of file structure:您可以拥有这种文件结构:

├── myModule
│    ├── myModule.client.js
│    ├── myModule.server.js
│    └── index.js           <-- difference
│ 
├── browser.js
└── server.js

And in index.js :index.js

if (process.browser) { // this condition can be different but you get the point
    module.exports = require('./myModule.client');
} else {
    module.exports = require('./myModule.server');
}

The main problem with this is that client bundle contains a lot of heavy kB backend code .这样做的主要问题是客户端包包含大量繁重的 kB 后端代码


My webpack configuration我的 webpack 配置

I include my webpack.config.js .我包括我的webpack.config.js Strangely this config always point to myModule.client.js for browser and node.奇怪的是,这个配置总是指向浏览器和节点的myModule.client.js

const webpack = require('webpack');
var path = require('path');
var fs = require('fs');

const DEBUG = !process.argv.includes('--release');
const VERBOSE = !process.argv.includes('--verbose');
const AUTOPREFIXER_BROWSERS = [
    'Android 2.3',
    'Android >= 4',
    'Chrome >= 35',
    'Firefox >= 31',
    'Explorer >= 9',
    'iOS >= 7',
    'Opera >= 12',
    'Safari >= 7.1',
];

let nodeModules = {};
fs.readdirSync('node_modules')
    .filter(function(x) {
        return ['.bin'].indexOf(x) === -1 ;
    })
    .forEach(function(mod) {
        nodeModules[mod] = 'commonjs ' + mod;
    });

let loaders = [
    {
        exclude: /node_modules/,
        loader: 'babel'
    },
    {
        test: [/\.scss$/,/\.css$/],
        loaders: [
            'isomorphic-style-loader',
            `css-loader?${DEBUG ? 'sourceMap&' : 'minimize&'}modules&localIdentName=` +
            `${DEBUG ? '[name]_[local]_[hash:base64:3]' : '[hash:base64:4]'}`,
            'postcss-loader?parser=postcss-scss'
        ]
    },
    {
        test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/,
        loader: 'url-loader',
        query: {
            name: DEBUG ? '[name].[ext]' : '[hash].[ext]',
            limit: 10000,
        },
    },
    {
        test: /\.(eot|ttf|wav|mp3)$/,
        loader: 'file-loader',
        query: {
            name: DEBUG ? '[name].[ext]' : '[hash].[ext]',
        },
    },
    {
        test: /\.json$/,
        loader: 'json-loader',
    },
];

const common = {
    module: {
        loaders
    },
    plugins: [
        new webpack.optimize.OccurenceOrderPlugin(),
    ],
    postcss: function plugins(bundler) {
        var plugins = [
            require('postcss-import')({ addDependencyTo: bundler }),
            require('precss')(),
            require('autoprefixer')({ browsers: AUTOPREFIXER_BROWSERS }),
        ];

        return plugins;
    },
    resolve: {
        root: path.resolve(__dirname, 'src'),
        extensions: ['', '.js', '.jsx', '.json']
    }
};


module.exports = [
    Object.assign({} , common, { // client
        entry: [
            'babel-polyfill',
            './src/client.js'
        ],
        output: {
            path: __dirname + '/public/',
            filename: 'bundle.js'
        },
        target: 'web',
        node: {
            fs: 'empty',
        },
        devtool: DEBUG ? 'cheap-module-eval-source-map' : false,
        plugins: [
            ...common.plugins,
            new webpack.DefinePlugin({'process.env.BROWSER': true }),
        ],
    }),
    Object.assign({} , common, { // server
        entry: [
            'babel-polyfill',
            './src/server.js'
        ],
        output: {
            path: __dirname + '',
            filename: 'server.js'
        },
        target: 'node',
        plugins: [
            ...common.plugins,
            new webpack.DefinePlugin({'process.env.BROWSER': false }),
        ],
        node: {
            console: false,
            global: false,
            process: false,
            Buffer: false,
            __filename: false,
            __dirname: false,
        },
        externals: nodeModules,

    })
];

The behavior is standardized here: https://github.com/defunctzombie/package-browser-field-spec行为在这里标准化: https : //github.com/defunctzombie/package-browser-field-spec

Although this specification is unofficial, many Javascript bundlers follow it, including Webpack, Browserify, and the React Native packager.尽管该规范是非官方的,但许多 Javascript 打包器都遵循它,包括 Webpack、Browserify 和 React Native 打包器。 The browser field not only allows you to change your module entry point, but to also replace or ignore individual files within your module.浏览器字段不仅允许您更改模块入口点,还可以替换或忽略模块中的单个文件。 It's quite powerful.它非常强大。

Since Webpack bundles code for the web by default, you need to manually disable the browser field if you want to use Webpack for your server build.由于 Webpack 默认为 Web 捆绑代码,如果您想将 Webpack 用于您的服务器构建,您需要手动禁用浏览器字段。 You can do that using the target config option to do this: https://webpack.js.org/concepts/targets/您可以使用target配置选项来做到这一点: https : //webpack.js.org/concepts/targets/

It has been a long time since this question was asked.问这个问题已经很长时间了。 I just want to clarify the previous answer.我只是想澄清以前的答案。

If you look at tools/webpack.config.js in React Starter Kit you will see that it exports two Webpack configurations that slightly differ, eg module.exports = [clientConfig, sererConfig].如果您查看 React Starter Kit 中的 tools/webpack.config.js,您会看到它导出了两个略有不同的 Webpack 配置,例如 module.exports = [clientConfig, sererConfig]。 The server-side bundle config has this field target set to node (by default it's web).服务器端捆绑配置将此字段目标设置为节点(默认情况下它是网络)。

It seems this webpack beheavior is not documented, but webpack automatically takes 'main' entry when target is 'node' and takes 'browser' entry when target is 'web'.似乎没有记录此 webpack 行为,但是当目标是“节点”时,webpack 会自动获取“主”条目,而当目标是“web”时,它会自动获取“浏览器”条目。

If you look at tools/webpack.config.js in React Starter Kit you will see that it exports two Webpack configurations that slightly differ, eg module.exports = [clientConfig, sererConfig] .如果您查看React Starter Kit中的tools/webpack.config.js ,您会看到它导出了两个略有不同的 Webpack 配置,例如module.exports = [clientConfig, sererConfig] The server-side bundle config has this field target set to node (by default it's web ).服务器端捆绑配置将此字段target设置为node (默认情况下为web )。

https://webpack.github.io/docs/configuration.html#target https://webpack.github.io/docs/configuration.html#target

The approach that you described works great for modules that have exactly the same API but different implementations, like in the case with HTTP client utility that uses XMLHttpRequest in its browser-specific implementation and Node's http module in its server implementation:您描述的方法非常适用于具有完全相同 API 但实现不同的模块,例如 HTTP 客户端实用程序在其特定于浏览器的实现中使用XMLHttpRequest以及在其服务器实现中使用 Node 的http模块的情况:

https://github.com/kriasoft/react-starter-kit/tree/master/src/core/fetch https://github.com/kriasoft/react-starter-kit/tree/master/src/core/fetch

To have a different entry point for client and server in a Node Module, you can use process.browser flag and handle the same要在节点模块中为客户端和服务器设置不同的入口点,您可以使用process.browser标志并处理相同的

if (process.browser) {
  // load client entry point
} else {
  // load server entry point
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 package.json中js浏览器库的入口点 - Entry point for a js browser lib in package.json package.json的main,module,browser属性是应该指向minified还是source? - Should main, module, browser properties of package.json point to minified or source? NPM package.json主要属性,没有合适的条目 - NPM package.json main attribute and no suitable entry 有没有办法有条件地更改package.json中的主条目文件? - Is there a way to conditionally change main entry file in package.json? 即使我在 package.json 中将主入口点更改为 app.js,为什么还需要 index.js? - Why index.js is necessary even if I change my main entry point to app.js in package.json? package.json 中 main 的路径应该指向哪里? - Where should the path from main in package.json point to? .min或src作为node_module的package.json中的“main”-file - .min or src as “main”-file in package.json for node_module 在 Mongoose 中,找不到模块 '\node_modules\ipaddr.js\lib\ipaddr.js'。 请确认 package.json 具有有效的“主”条目 - In Mongoose, can't find module '\node_modules\ipaddr.js\lib\ipaddr.js'. Please verify that the package.json has a valid “main” entry 如何使用package.json中的'main'参数? - How to use the 'main' parameter in package.json? 清理 package.json &quot;main&quot; 以与 require 一起使用 - Cleaning package.json "main" for use with require
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM