Vue3集成Webpack+Django时SFC中v-model失效问题求助
在Vue3环境下,使用字符串模板(格式如template: '<div>....')时v-model功能正常,但使用单文件组件的<template>元素时v-model完全失效。当前项目通过自定义Webpack配置与Django集成,相关配置如下:
Webpack配置
const path = require('path'); var webpack = require("webpack"); var BundleTracker = require('webpack-bundle-tracker'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); const { VueLoaderPlugin } = require("vue-loader"); const entries = { home:'./main/static/main/js/pages/home.js', offer:'./main/static/main/js/pages/offer.js', profile:'./main/static/main/js/pages/profile.js', request:'./main/static/main/js/pages/request.js', navbar:'./main/static/main/js/pages/navbar.js', } module.exports = { entry:entries, // 输入文件路径 output: { clean: false, filename: '[name].[contenthash].js', // 输出bundle文件名 chunkFilename: '[name].[contenthash].js', path: path.resolve(__dirname, './main/static/main/builds'), // Django静态文件目录路径 }, resolve: { alias: { vue: 'vue/dist/vue.js', '@':path.resolve(__dirname, './main/static/main/js'), }, }, plugins: [ new webpack.DefinePlugin({ 'process.env.ENV': JSON.stringify(process.env.ENV), }), new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery" }), new BundleTracker({ path: __dirname, filename: './main/static/main/builds/webpack-stats.json', }), new MiniCssExtractPlugin(), new VueLoaderPlugin() ], optimization: { minimizer:[ new CssMinimizerPlugin(), new TerserPlugin(), // new CompressionPlugin() ], minimize: true, }, module: { rules: [ { test:/\.vue$/, loader: 'vue-loader' }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/, options:{ cacheDirectory: true } }, { test: /\.(png|svg|jpg|gif)$/i, use: [ { loader: 'url-loader', options: { limit: 8192, }, }, ], }, { test: /\.(png|jpe?g|gif)$/i, use: [ { loader: 'file-loader', }, ], }, { test: /\.s[ac]ss$/i, use:[ { loader: MiniCssExtractPlugin.loader, }, { loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'postcss-loader', options: { postcssOptions: { plugins: () => [ require('autoprefixer') ] } } }, { loader: 'sass-loader' } ], }, ] }, resolveLoader: { modules: [ path.join(__dirname,'node_modules') ] }, resolve: { modules: [ path.join(__dirname, 'node_modules') ] } };
package.json配置
{ "name": "bricoram", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack -w --mode=development", "build": "webpack --mode=production" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.15.5", "@babel/preset-env": "^7.15.6", "@babel/preset-react": "^7.14.5", "@vue/compiler-sfc": "^3.2.41", "autoprefixer": "^10.4.13", "babel-loader": "^8.2.2", "css-loader": "^6.7.1", "file-loader": "^6.2.0", "install": "^0.13.0", "js-cookie": "^3.0.1", "mini-css-extract-plugin": "^2.6.1", "npm": "^8.5.2", "postcss": "^8.4.7", "postcss-loader": "^6.2.1", "requirejs": "^2.3.6", "sass": "^1.39.2", "sass-loader": "^12.1.0", "style-loader": "^3.2.1", "toastr": "^2.1.4", "url-loader": "^4.1.1", "v-viewer": "^3.0.11", "vue-loader": "^17.0.0", "vue-multiselect": "^2.1.6", "vue-style-loader": "^4.1.3", "vue-template-compiler": "^2.7.13", "vue-toast-notification": "^0.6.3", "webpack": "^5.52.1", "webpack-bundle-tracker": "^1.6.0", "webpack-cli": "^4.8.0" }, "dependencies": { "@fortawesome/fontawesome-free": "^6.0.0-beta2", "@gitlab/svgs": "^1.218.0", "@popperjs/core": "^2.11.6", "@sentry/browser": "^6.18.2", "@sentry/tracing": "^6.18.2", "@tarekraafat/autocomplete.js": "^10.2.7", "axios": "^0.27.2", "bootstrap": "^5.2.2", "compression-webpack-plugin": "^10.0.0", "css-minimizer-webpack-plugin": "^4.2.2", "glob": "^8.0.3", "jquery": "^3.6.0", "jquery-ui": "^1.13.0", "jquery-ui-bundle": "^1.12.1-migrate", "just-detect-adblock": "^1.1.0", "moment": "^2.29.1", "pikaday": "^1.8.2", "terser-webpack-plugin": "^5.3.6", "viewerjs": "^1.10.1", "vue": "^3.2.41", "vue-template-loader": "^1.1.0", "vue3-sfc-loader": "^0.8.4", "webpack-jquery-ui": "^2.0.1" }, "browserslist": [ "> 1%", "last 2 versions", "not dead", "not ie 11" ] }
问题原因及修复方案
1. Webpack resolve配置被覆盖
配置中定义了两次resolve对象,第二个会完全覆盖第一个,导致alias(包括Vue和@的别名)全部失效,Webpack无法正确解析Vue3模块路径,引发SFC编译异常。
修复:
合并两个resolve配置:
resolve: { alias: { vue: 'vue/dist/vue.esm-bundler.js', // 修正Vue3的正确ESM入口 '@': path.resolve(__dirname, './main/static/main/js'), }, modules: [ path.join(__dirname, 'node_modules') ] },
2. Vue2与Vue3模板编译器冲突
package.json中同时安装了Vue3的@vue/compiler-sfc和Vue2的vue-template-compiler,两者冲突导致SFC的<template>无法被正确编译为Vue3兼容代码,v-model语法解析失败。
修复:
卸载Vue2的模板编译器:
npm uninstall vue-template-compiler
3. CSS加载器功能互斥
Sass规则中同时使用MiniCssExtractPlugin.loader(提取CSS为独立文件)和style-loader(将CSS注入JS),两者功能冲突,间接影响组件渲染。
修复:
根据环境区分使用加载器,生产环境用提取器,开发环境用热重载的style-loader:
{ test: /\.s[ac]ss$/i, use: [ process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader', 'css-loader', { loader: 'postcss-loader', options: { postcssOptions: { plugins: () => [require('autoprefixer')] } } }, 'sass-loader' ] }
4. Vue别名指向错误
原配置中vue别名指向Vue2的UMD版本入口vue/dist/vue.js,Vue3的正确ESM入口应为vue/dist/vue.esm-bundler.js,该版本支持编译时特性,能正确处理v-model语法。
修复:
修改别名指向上述正确路径,或直接移除别名让Webpack自动解析Vue3默认入口。
5. 重复图片加载规则
配置中有两条几乎相同的图片处理规则,会导致文件重复处理,建议合并为一条:
{ test: /\.(png|svg|jpe?g|gif)$/i, use: [ { loader: 'url-loader', options: { limit: 8192, // 小于8kb转base64,大于则用file-loader处理 fallback: 'file-loader' } } ] }
完成以上修改后,重新执行npm run dev或npm run build,SFC中的v-model即可恢复正常。
内容的提问来源于stack exchange,提问作者user2080105

