Django + webpack 测试开发环境搭建

濮君植
2023-12-01

背景与问题

最近在捯饬一个东西:

  • 后台服务由 Django 提供
  • 视图使用 Django 的模板进行处理
  • 前端界面的一些逻辑,使用其他前端框架,例如 Vue

这意味着前端的代码需要使用 webpack 来进行打包,我遇到了两个问题:

  • 问题一:如何在 Django 的模板中,和 webpack 输出的动态 JS 文件挂钩?
  • 问题二:如何实现前后端代码的快速联调、测试?

django-webpack-loader

GitHub 上有一个项目,为上面的问题一提供了解决方案,这个方案的四路是这样的:

  • 使用一个“钩子”文件,在下面的示例中,这个文件被命名为 webpack-stats.json
  • 当 webpack 生成了文件时,会将信息同步到这个文件中,包括 bundle 的名称、具体有哪些文件等等
  • 在 Django 的模板中使用相应标签,读取这个文件中的配置,在渲染过程中将 JS 文件名进行替换

安装

# 进入前端工程的文件夹
npm install --save-dev webpack-bundle-tracker
pip install django-webpack-loader

webpack 中的配置

这里的配置很简单,抛开其他设置不谈,主要就是要把 webpack 输出物的信息同步到一个配置文件中,在 webpack.config.json 中

var path = require('path');
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');

module.exports = {
  entry: ...,
  output: ..., // 输出的位置需要是 Django 中 static 文件的目录,

  plugins: [
    new BundleTracker({
        path: ..., // 钩子文件的位置
        filename: './webpack-stats.json' // 钩子文件的名称
    }) 
  ]
}

Django 中的配置

主要就是要启用 django-webpack-loader 应用,并且做一些配置

# 开启 webpack-loader,找到 INSTALLED_APPS
INSTALLED_APPS = [
    ...,
    'webpack_loader',
]

# 对 static 文件进行配置,static 文件就是 Django 用来放静态文件的地方
STATIC_URL = '/static/'
STATIC_ROOT =  '/完整路径/static/'

# webpack-loader 的配置
WEBPACK_LOADER = {
    'DEFAULT': {
        'CACHE': not DEBUG, # 是否使用缓存,建议 no
        'BUNDLE_DIR_NAME': '', # webpack 输出文件,相对于 STATIC_ROOT 的相对位置,需要以 “/” 结尾
        'STATS_FILE': '/完整路径/webpack-stats.json', # 钩子文件的地址
    }
}

如果存在多个 webpack 项目,就需要多个钩子文件,那么就要修改 WEBPACK_LOADER 配置

WEBPACK_LOADER = {
    'DEFAULT': { # 需要有一个名为 DEFAULT 的配置
        'BUNDLE_DIR_NAME': '路径', # webpack 输出文件,相对于 STATIC_ROOT 的相对位置,需要以 “/” 结尾
        'STATS_FILE': '/完整路径/webpack-stats.json', # 钩子文件的地址
    },
    'AnotherProject': {
        'BUNDLEDIR_NAME': '路径', # webpack 输出文件,相对于 STATIC_ROOT 的相对位置,需要以 “/” 结尾
        'STATS_FILE': '/完整路径/webpack-stats.json', # 钩子文件的地址
    }
}

在 Django 模板中引用 JS 文件

<!--引入 render_bundle-->
{% load render_bundle from webpack_loader %}

<html>
  <head>
    <!--渲染 bundle 名为 main 的、结尾是 css 的、在配置项 DEFAULT 中描述的相关文件-->
    {% render_bundle 'main' 'css' 'DEFAULT'%}
    <!--bundle 名可以在钩子文件中查看,或者根据 webpack 的设置查看-->
    <!--css 是文件的后缀,还支持 js、jsx、scss、sass-->
    <!--DEFAULT 就是我们在 settings.py 中的配置-->
  </head>
  <body>
    ...
  </body>
</html>

这里有一些 github 项目中没有说明的地方,就是我们某一个 webpack 配置输出的文件,很可能后缀名相同,但是需要插入到文件的不同位置,那么该怎么办呢?
我看了源码之后发现了一个折中的方法:

  • 在 webpack 配置中,对 output 的文件名称做一些区分,例如需要放在头部的文件命名为 xxx.head.js,需要放在 body 中的文件命名为 xxx.body.js,那么在 Django 模板中,我们就可以这样写
<!--引入 render_bundle-->
{% load render_bundle from webpack_loader %}

<html>
  <head>
    {% render_bundle 'main' 'head.js' 'DEFAULT'%}
  </head>
  <body>
    ...
    {% render_bundle 'main' 'body.js' 'DEFAULT'%}
  </body>
</html>

bug 处理

如果你使用的是 webpack v5,你可能会在调试过程中遇到问题,无法成功加载,原因是最新版的 webpack-bundle-tracker 生成的文件格式发生了变更,但是 django-webpack-loader 没有做对应的调整,可以参考这个 Issue 中描述的方法

# 你可以通过 pip show django-webpack-loader 找到你 django-webpack-loader 安装位置
# .../site-packages/webpack_loader/loader.py

# 删除下面这一行
return self.filter_chunks(chunks)

# 在删除的位置添加这几行
if 'assets' in assets:
    return self.filter_chunks(list(map(assets['assets'].get, chunks)))
else:
    return self.filter_chunks(chunks)

django + webpack 联调测试环境搭建

webpack 提供了热更新(HMR)的功能,这样就不用每次写完一些代码以后,必须要等待编译、输出后才能看到效果,而是文件修改已经保存,就能在内存中快速更新,并可以在配置的地址和端口上服务。
Django 也提供了快速调试的功能,也就是 python manage.py runserver,也是在文件修改保存时,能够快速应用修改,虚无等待。
唯一的问题是 Django 的 runserver 和 webpack 的 HMR 只能跑在不同的端口上,而在 Django 的模板文件中,相关静态文件的引用都是直接使用的“绝对路径”,因此会出现问题。
于是我用了 nginx 做了一次转发,解决了这个问题。

webpack 的 HMR 配置

首先要安装

# 进入前端项目工程目录
npm install --save-dev webpack-dev-server

在 webpack.config.json 中

const webpack = require('webpack');

const hotReload = process.env.HOT_RELOAD === '1'; // 是否开启了 hotReload

module.exports = {
  entry: ...,
  output: {
      ...,
      path: 'Django 设置的 STATIC_ROOT'
      publicPath: hotReload? 'Django 设置的 STATIC_URL': ''//如果是 hotReload 状态,那么应该输出到 Django 设置的 STATIC_URL,如果是输出静态文件,那么输出的文件地址要变更为 ''
  },
  devServer: { // 对 HMR 服务器的设置
    hot: true,
    quiet: false,
    headers: { 'Access-Control-Allow-Origin': '*' },
    port: 8001 // 端口可以设置成你想要的
  },
  plugin: [
    ...,
    new webpack.HotModuleReplacementPlugin(),
  ]
};

同时在 package.json 中设置对应的指令

{
    "scripts": {
		"start": "HOT_RELOAD=1 webpack-dev-server --mode development",
    	"build": "NODE_ENV=production webpack --mode production",
    }
}

nginx 的配置

server {
    listen      8002; # nginx 监听地址
    charset     utf-8;

    location /media  {
        alias media_文件的路径;  # media 文件用来放图片之类纯粹静态的文件
    }

    location /static {
	    proxy_pass http://127.0.0.1:8001; # 对 static 的请求转发给 webpack 的 HMR devServer
    }

    location / {
	    proxy_pass http://127.0.0.1:8000; # 其他路径的请求转发给 python manage.py runserver 的端口
    }
}

配置完成后, sudo nginx -s reload 一下,重新加载配置文件

开始调试

# 新开一个终端,或者用 tmux
# 进入前端项目路径
npm run start
# 新开一个终端,或者用 tmux
# 进入 Django 项目路径
python manage.py runserver 8000

这样你就可以通过 http://localhost:8002 看到运行的效果了。

 类似资料: