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

标签 javascript reactjs webpack isomorphic-javascript react-starter-kit

在同构 React 应用程序中,我有 myModule,它在节点和浏览器环境中的行为应该不同。我想在 package.json 中为 myModule 配置这个分割点:

package.json

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

文件结构

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

所以当我在 node 中使用 myModule 时,我应该只得到 myModule.server.js:

server.js

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

浏览器端应该仅使用myModule.client.js构建包:

浏览器.js

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

react-starter-kit使用这种方法,但我不知道这个配置在哪里定义。


动机

  1. package.json 是进行这种拆分的良好语义点。
  2. 客户端包仅包含 myModule.client.js

已知的解决方案 - 不是我的答案

你可以有这样的文件结构:

├── myModule
│    ├── myModule.client.js
│    ├── myModule.server.js
│    └── index.js           <-- difference
│ 
├── browser.js
└── server.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');
}

这个的主要问题是客户端包包含大量沉重的 kB 后端代码


我的webpack配置

我包含了我的 webpack.config.js。奇怪的是,这个配置总是指向浏览器和节点的 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,

    })
];

最佳答案

这里的行为是标准化的:https://github.com/defunctzombie/package-browser-field-spec

虽然此规范不是官方规范,但许多 Javascript 打包器都遵循它,包括 Webpack、Browserify 和 React Native 打包器。浏览器字段不仅允许您更改模块入口点,还可以替换或忽略模块中的单个文件。它非常强大。

由于 Webpack 默认捆绑了 web 代码,如果你想使用 Webpack 构建服务器,你需要手动禁用浏览器字段。您可以使用 target 配置选项来做到这一点:https://webpack.js.org/concepts/targets/

关于javascript - 节点和浏览器在 package.json 中的不同主要入口点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38055510/

相关文章:

javascript - 在 HTML canvas 上一条接一条地绘制连续的线条

javascript - 从浏览器控制台 JS 调用类(在模块中定义)时,'Uncaught ReferenceError : . .. 未定义'

javascript - 找不到模块 : Error: Can't resolve '../fonts/fontawesome-webfont.eot'

javascript - 如何修复未捕获的类型错误 : Cannot read property 'name' of undefined

javascript - 开 Jest 监视功能

webpack - 在 Windows 上的 Ubuntu 上的 Bash 中使用 watch 运行 webpack 构建一次并退出

reactjs - React 微前端与 Module Federation

javascript - 当字体太小时 &lt;input&gt; 的按钮会上升

javascript - 确定 ECMAScript 赋值表达式的左侧或右侧

javascript - 在没有节点的网页中使用 Require 和 React