Django/Webpack - 如何使用webpack dev server提供生成的webpack捆绑包

12
Django的'static'标签使用STATIC_URL生成URL,这会得到类似'/static/myapp/js/bundle.js'的结果。与此同时,webpack-dev-server正在从'url localhost:3000'提供bundle。
我的问题是如何使Django 'static'模板标签生成不同的URL(指向webpack dev server)用于js bundles。当然我可以在模板中硬编码它,但这不是一个好的解决方案。
以下是我的项目配置
webpack.config.js
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const BundleTracker = require('webpack-bundle-tracker')


module.exports = {
    mode: 'development',
    context: path.dirname(path.resolve(__dirname)),
    entry: {
        index: './typescript_src/index.ts',
    },
    output: {
        path: path.resolve('./myproject/assets/myapp/bundles/'),
        filename: "[name]-[hash].js"
    },
    resolve: {
        extensions: ['.ts', '.js' ]
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.ts$/,
                use: 'ts-loader',
                exclude: /node_modules/
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin(),
        new BundleTracker({filename: './myproject/webpack-stats.json'})
    ],
    devServer: {
        port: 3000,
        publicPath: '/myapp/bundles/',
        // hot: true,
        headers: {
            "Access-Control-Allow-Origin": "http://127.0.0.1:8000", /**Django dev server */
        }
    }
}

设置.py

WEBPACK_LOADER = {
    'DEFAULT': {
        'CACHE': not DEBUG,
        'BUNDLE_DIR_NAME': 'myapp/bundles/', # must end with slash
        'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
        'POLL_INTERVAL': 0.1,
        'TIMEOUT': None,
        'IGNORE': [r'.+\.hot-update.js', r'.+\.map']
    }
}

STATIC_URL = '/static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'assets'),
)

最初,我决定在开发期间让webpack也能为其他静态文件提供服务

webpack.config.js

devServer: {
        port: 3000,
        publicPath: '/myapp/bundles/',
        contentBase: path.resolve('./myproject/assets')
        // hot: true,
        headers: {
            "Access-Control-Allow-Origin": "http://127.0.0.1:8000", /**Django dev server */
        }

设置.py

# in development mode serve from wepack dev server
if DEBUG:
    STATIC_URL = 'http://localhost:3000/'
else:
    STATIC_URL = '/static/'

但后来我意识到我必须提供其他应用(如admin、tinymce等)的静态文件,而这对于webpack Dev服务器是不可能实现的。

问题在于由django-webpack-loader的'render_bundle'标签生成的url (/static/myapp/bundles/bundle-name.js) 会导致Http 404,因为webpack-dev-server将生成的bundle保存在内存中而非磁盘上。

此外,如果我设置

STATIC_URL = localhost:3000

并配置webpack-dev-server以服务于我的应用程序的其他静态文件,不会提供其他应用程序的静态文件

3个回答

11

让我们分析这个问题:

我们有两台服务器,希望根据请求的路径将请求路由到其中一台服务器:

"/static/webpackbundles/** ==> webpack dev server

其他路径 ==> django dev server

这正是代理服务器的工作,可以使用第三方服务器(haproxy、nginx...)来实现,但这可能会显得过于复杂,特别是如果我们知道webpack dev server可以用作代理! (https://webpack.js.org/configuration/dev-server/#devserverproxy)

webpack.config.js

const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: '/path/to/django_project/django_project/static/webpackbundles',
    publicPath: '/static/webpackbundles/',
  },
  devServer: {
    contentBase: '/path/to/django_project/django_project/static/webpackbundles',
    hot: true,
    proxy: {
      '!/static/webpackbundles/**': {
        target: 'http://localhost:8000', // points to django dev server
        changeOrigin: true,
      },
    },
  },
};
在你的 Django 模板中:
<script type="text/javascript" src="{% static 'webpackbundles/main.js' %}"></script>

现在使用webpack dev server地址访问您的Django应用程序/站点: 例如:http://localhost:8081

通过这个简单的配置,您将拥有浏览器自动刷新和热模块替换功能。 您不需要更改任何Django代码,也不需要使用django-webpack-loader。


你的解决方案很好并且已经奏效了,谢谢。但是我们仍需要django-webpack-loader来呈现正确的捆绑包名称,因为生成的捆绑包名称是以[name]-[hash].js的格式。当然,如果我们不将[hash]部分添加到捆绑包名称中,我们可以不使用django-webpack-loader。 - myke
1
不需要使用webpack的哈希,Django可以做到(这将确保非webpack静态文件也具有哈希),只需将以下内容添加到您的Django设置文件中:STATICFILES_STORAGE ='django.contrib.staticfiles.storage.ManifestStaticFilesStorage'(请参见ManifestStaticFilesStorage)。这将为您节省添加/维护额外Django包的麻烦。 - Ejez
不知道ManifestStaticFilesStorage。但是看文档,似乎需要DEBUG=False和其他要求。这仍然让我觉得如果webpack做添加哈希部分会更自然。但还是很好知道Django有这个功能。也许以后会派上用场。 - myke
1
你还可以使用 html-webpack-plugin。它会自动将 webpack 打包后的 js、css 文件等注入到生成的 HTML 文件中。这些文件可以从 Django 的 base.html 模板继承,也可以进一步扩展来创建最终的 Django 页面模板。详情请参见此处此处,我认为这比使用 django-webpack-loader 要好得多。@myke - Ejez

0
在 @Ejez 的回答基础上,我成功地配置了 webpack-dev-server 以服务于所有静态文件(包括媒体文件)。

webpack.config.js

module.exports = {
    // project root (usually package.json dir)
    context: path.dirname(path.resolve(__dirname)),
    output: {
        path: path.resolve('./path/to/bundles/'), 
        filename: "[name]-[hash].js"
    },
    resolve: {
        extensions: ['.ts', '.js' ]
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.ts$/,
                use: 'ts-loader',
                exclude: /node_modules/
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin(),
        new BundleTracker({filename: '/path/to/webpack-stats.json'})
    ],
    optimization: {
        splitChunks: {
            chunks: 'all',
            name: 'lib'  // bundle all their party libraries in lib.js
        }
    },
    devServer: {
    // if you do not mind webpack serving static files of other apps
    // collect them (with django collectstatic) into /static_root/static/ 
    // this way webpack-dev-server can serve from your own app's /static/
    // directory and also /static_root/static/ directory (which contains
    // static files of other apps
        contentBase: [path.resolve('./project'), path.resolve('./project/static_root')],

        // webpack bundles will be served from http://locahost:3000/static/project/bundles/
        publicPath: '/static/project/bundles/',

        port: 3000,

        // proxy all request except (static and media files) to django dev server
        proxy: [{
            context: ['**', '!/static/**', '!/media/**'],
            target: 'http://localhost:8000',
            changeOrigin: true,
        }]
    }
}

现在你可以通过webpack-dev-server的URL localhost:3000访问你的项目。不要忘记启动dev服务器(webpack和django)。


1
这似乎对于快速开发有点低效,因为每次修改非Webpack静态文件时都需要收集静态文件。为什么不保持简单,让webpack-dev-server仅提供webpack内容,并将请求代理到django dev服务器的django内容呢? - Ejez
如果每次更改应用程序中的静态文件都必须收集静态文件,那么这将是一个问题。但是我的意思是只需收集一次以访问其他应用程序的静态文件,并且每次安装第三方应用程序(偶尔发生)时都需要进行收集。 - myke

0

通常不建议使用STATICFILES_DIRS,而是使用STATIC_ROOT。 如果您有将静态文件复制到项目文件夹的权限,则使用STATIC_ROOT。


1
你的回答可以通过提供额外的支持信息来改进。请进行[编辑]以添加更多细节,例如引用或文档,以便他人可以确认您的答案正确无误。您可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接