Laravel Mix生产构建报CssSyntaxError,求单次构建解决方案
问题背景
我在Laravel项目里开发Vue SPA,用Laravel Mix做资源构建,之前npm run watch和npm run development都运行正常,但引入vue2-daterange-picker后,执行npm run production时抛出了CssSyntaxError错误,提示CSS文件缺少分号。分开构建CSS和JS能成功,但希望一次性完成构建。
错误日志
> @ production /my-project > cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js CssSyntaxError: /css/app.css:3:19: Missed semicolon at Input.error (/my-project/node_modules/postcss/lib/input.js:130:16) at Parser.checkMissedSemicolon (/my-project/node_modules/postcss/lib/parser.js:603:22) at Parser.decl (/my-project/node_modules/postcss/lib/parser.js:286:46) at Parser.other (/my-project/node_modules/postcss/lib/parser.js:166:12) at Parser.parse (/my-project/node_modules/postcss/lib/parser.js:77:16) at parse (/my-project/node_modules/postcss/lib/parse.js:17:12) at new LazyResult (/my-project/node_modules/postcss/lib/lazy-result.js:60:16) at Processor.<anonymous> (/my-project/node_modules/postcss/lib/processor.js:138:12) at Processor.process (/my-project/node_modules/postcss/lib/processor.js:117:23) at Function.creator.process (/my-project/node_modules/postcss/lib/postcss.js:148:43) at OptimizeCssAssetsWebpackPlugin.processCss (/my-project/node_modules/optimize-css-assets-webpack-plugin/src/index.js:73:21) at Object.processor (/my-project/node_modules/optimize-css-assets-webpack-plugin/src/index.js:13:18) at each (/my-project/node_modules/last-call-webpack-plugin/src/index.js:150:10) at arrayEach (/my-project/node_modules/lodash/_arrayEach.js:15:9) at forEach (/my-project/node_modules/lodash/forEach.js:38:10) at OptimizeCssAssetsWebpackPlugin.process (/my-project/node_modules/last-call-webpack-plugin/src/index.js:147:5) at compilation.hooks.optimizeChunkAssets.tapPromise.chunks (/my-project/node_modules/last-call-webpack-plugin/src/index.js:178:28) at _next0 (eval at create (/my-project/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:9:17) at _err0 (eval at create (/my-project/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:25:1) at taskRunner.run (/my-project/node_modules/terser-webpack-plugin/dist/index.js:321:9) at step (/my-project/node_modules/terser-webpack-plugin/dist/TaskRunner.js:87:9) at _cacache.default.get.then (/my-project/node_modules/terser-webpack-plugin/dist/TaskRunner.js:111:15) at tryCatcher (/my-project/node_modules/bluebird/js/release/util.js:16:23) at Promise._settlePromiseFromHandler (/my-project/node_modules/bluebird/js/release/promise.js:547:31) at Promise._settlePromise (/my-project/node_modules/bluebird/js/release/promise.js:604:18) at Promise._settlePromise0 (/my-project/node_modules/bluebird/js/release/promise.js:649:10) at Promise._settlePromises (/my-project/node_modules/bluebird/js/release/promise.js:729:18) at Promise._fulfill (/my-project/node_modules/bluebird/js/release/promise.js:673:18) at Promise._resolveCallback (/my-project/node_modules/bluebird/js/release/promise.js:466:57) at Promise._settlePromiseFromHandler (/my-project/node_modules/bluebird/js/release/promise.js:559:17) at Promise._settlePromise (/my-project/node_modules/bluebird/js/release/promise.js:604:18) at Promise._settlePromise0 (/my-project/node_modules/bluebird/js/release/promise.js:649:10) at Promise._settlePromises (/my-project/node_modules/bluebird/js/release/promise.js:729:18) at Promise._fulfill (/my-project/node_modules/bluebird/js/release/promise.js:673:18) at Promise._resolveCallback (/my-project/node_modules/bluebird/js/release/promise.js:466:57) at Promise._settlePromiseFromHandler (/my-project/node_modules/bluebird/js/release/promise.js:559:17) npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! @ production: `cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the @ production script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! /home/user/.npm/_logs/2019-11-09T04_25_23_448Z-debug.log
相关配置
package.json
{ "private": true, "scripts": { "dev": "npm run development", "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", "watch-poll": "npm run watch -- --watch-poll", "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", "prod": "npm run production", "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" }, "devDependencies": { "@babel/plugin-syntax-dynamic-import": "^7.2.0", "axios": "^0.19.0", "bootstrap": "^4.3.1", "cross-env": "^6.0.3", "dateformat": "^3.0.3", "jquery": "^3.4.1", "laravel-mix": "^5.0.0", "laravel-mix-polyfill": "^1.1.0", "lodash": "^4.17.15", "popper.js": "^1.16.0", "resolve-url-loader": "^3.1.0", "sass": "^1.23.3", "sass-loader": "^8.0.0", "vue": "^2.6.10", "vue-i18n": "^8.15.0", "vue-router": "^3.1.3", "vue-template-compiler": "^2.6.10", "vue2-daterange-picker": "^0.3.1", "vuex": "^3.1.1" }, "dependencies": {} }
webpack.mix.js
let mix = require('laravel-mix'); require('laravel-mix-polyfill'); /* |-------------------------------------------------------------------------- | Mix Asset Management |-------------------------------------------------------------------------- | | Mix provides a clean, fluent API for defining some Webpack build steps | for your Laravel application. By default, we are compiling the Sass | file for the application as well as bundling up all the JS files. | */ mix.version(); mix.babelConfig({ plugins: ['@babel/plugin-syntax-dynamic-import'], }); mix.webpackConfig({ output: { chunkFilename: 'js/[name].[chunkhash].js', }, }); mix.polyfill({ enabled: true, targets: [ 'cover 99.5%', ], }); mix.js('resources/assets/js/app.js', 'public/js'); mix.sass('resources/assets/sass/app.scss', 'public/css');
解决方案
这个问题的核心是生产环境下CSS压缩插件(OptimizeCssAssetsWebpackPlugin)会严格校验CSS语法,而vue2-daterange-picker的CSS文件可能存在语法不规范的地方,开发环境下不会触发严格校验所以没问题。下面是几个可行的解决方法:
1. 配置CSS压缩插件以安全模式处理CSS
这是最推荐的方法,既能保留CSS压缩功能,又能忽略非致命的语法问题。修改webpack.mix.js,手动配置OptimizeCssAssetsPlugin,添加safe: true选项:
let mix = require('laravel-mix'); require('laravel-mix-polyfill'); const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); mix.version(); mix.babelConfig({ plugins: ['@babel/plugin-syntax-dynamic-import'], }); mix.webpackConfig({ output: { chunkFilename: 'js/[name].[chunkhash].js', }, plugins: [ new OptimizeCssAssetsPlugin({ cssProcessorOptions: { safe: true, // 启用安全模式,忽略非致命语法错误 discardComments: { removeAll: true }, // 移除所有注释,不影响功能 }, }) ] }); mix.polyfill({ enabled: true, targets: [ 'cover 99.5%', ], }); mix.js('resources/assets/js/app.js', 'public/js'); mix.sass('resources/assets/sass/app.scss', 'public/css');
修改后重新执行npm run production即可。
2. 单独引入组件的CSS文件
如果组件内部引入CSS导致语法问题,可以改为在app.scss中直接引入组件的CSS文件,让Laravel Mix统一处理:
在resources/assets/sass/app.scss中添加:
@import '~vue2-daterange-picker/dist/vue2-daterange-picker.css';
同时确保Vue组件中不再重复引入该CSS文件,避免样式冲突。
3. 降级vue2-daterange-picker版本
当前使用的0.3.1版本可能存在CSS语法问题,尝试降级到更早的稳定版本:
npm uninstall vue2-daterange-picker npm install vue2-daterange-picker@0.2.9 --save-dev
4. 临时禁用CSS压缩(不推荐长期使用)
如果以上方法都无效,可以临时关闭CSS压缩功能,这样能绕过语法校验,但会导致CSS体积变大:
在webpack.mix.js中添加:
mix.options({ cssNano: false, });
内容的提问来源于stack exchange,提问作者Yz.

