likes
comments
collection
share

一次uniapp HbuilderX创建的小程序项目中使用tailwindcss折腾记录

作者站长头像
站长
· 阅读数 13

一次uniapp HbuilderX创建的小程序项目中使用tailwindcss折腾记录

如题,是基于HbuilderX创建的小程序项目,非cli方式创建的项目

1.啰嗦两句

  • tailwindcss已经出来很长时间了,在前端娱乐圈里也是炒的火热。这个工具还没使用过,windicss这个新的轮子又都扎我脸上了。
  • 之前被uniapp HbuilderX方式创建的h5项目折腾过,这篇文章《uniapp 打包 h5 问题总结》有记录。那这次为什么还要用HbuilderX这种方式,没办法,一开始项目不是我创建的。

省流(得出结论):

  • uniapp HbuilderX方式被高度封装,都被封装到他的应用里去了,虽暴露出来的几个文件和方法,后续配置起来还是费劲。
  • 而用vue-cli方式创建的,配置文件全部暴露,方便后续修改

2.开始配置

  • 根据这位兄弟的分享,Hbuilder创建的uniapp工程,使用tailwindcss最优雅的方式 提示,HbuilderX创建的uniapp工程也是内置了postcss,但都是高度封装的。
  • 想基于它内部的postcss中添加tailwindcss plugins,很难,路会走偏(亲测,路确实很偏,一堆报错,还会和uview-ui冲突)
  • 所以这里直接使用Tailwind CLI方式

根据文档

  1. npm install -D tailwindcss安装

  2. npx tailwindcss init,生成tailwind.config.js 配置文件

/** @type {import('tailwindcss').Config} */
module.exports = {
	// https://ask.dcloud.net.cn/article/40098
  	separator: '__', // 如果是小程序项目需要设置这一项,将 : 选择器替换成 __,之后 hover:bg-red-500 将改为 hover__bg-red-500  
  	corePlugins: {  
  		// 预设样式  
  		preflight: false, // 一般uniapp都有预设样式,所以不需要tailwindcss的预设  
  		// 以下功能小程序不支持  
  		space: false, // > 子节点选择器  
  		divideWidth: false,  
  		divideColor: false,  
  		divideStyle: false,  
  		divideOpacity: false,  
  	},
	  content: [
		  './pages/**/*.{vue,js}',
		  // './main.js',  
		  './App.vue',  
		  // './index.html' 
	  ],
	  theme: {
		extend: {},
	  },
	  plugins: [],
}
  1. 根目录新建tailwind-input.css
/* @tailwind base; */
@tailwind components;
@tailwind utilities;
  1. 开启 Tailwind CLI 构建流程
npx tailwindcss -i ./tailwind-input.css -o ./static/css/tailwind.css --watch
  1. App.vue中引入编译过的tailwind.css
<style lang="scss">
	@import "@/uni_modules/uview-ui/index.scss";
+	@import url("./static/css/tailwind.css");
</style>

其实到这里已经ok了,缺点就是每次运行项目都要自己手动去执行npx tailwindcss -i ./tailwind-input.css -o ./static/css/tailwind.css --watch,不方便 所以必须改成自动化

3.启动项目自动化tailwindcss编译

庆幸的是,uniapp官方暴露出来了vue.config.js,我们可以在这里面配置

3.1 package.json中添加自定义脚本运行

{
	"scripts": {
		"tailwind:dev": "npx tailwindcss -i ./tailwind-input.css -o ./static/css/tailwind.css --watch",
		"tailwind:prod": "npx tailwindcss -i ./tailwind-input.css -o ./static/css/tailwind.css"
	},
	"uni-app": {
		"scripts": {
			"dev:mp-weixin:dev": {
				"title": "本地开发-测试环境接口",
				"browser": "",
				"env": {
					"UNI_PLATFORM": "mp-weixin",
					"NODE_ENV": "development",
					"BASE_URL_ENV": "development"
				},
				"define": {
					"CUSTOM-CONST": true
				}
			},
			"dev:mp-weixin:prod": {
				"title": "本地开发-正式环境接口",
				"browser": "",
				"env": {
					"UNI_PLATFORM": "mp-weixin",
					"NODE_ENV": "development",
					"BASE_URL_ENV": "production"
				},
				"define": {
					"CUSTOM-CONST": true
				}
			},
			"build:mp-weixin:dev": {
				"title": "打包-测试环境接口",
				"browser": "",
				"env": {
					"UNI_PLATFORM": "mp-weixin",
					"NODE_ENV": "production",
					"BASE_URL_ENV": "development"
				},
				"define": {
					"CUSTOM-CONST": true
				}
			},
			"build:mp-weixin:prod": {
				"title": "打包-正式环境接口",
				"browser": "",
				"env": {
					"UNI_PLATFORM": "mp-weixin",
					"NODE_ENV": "production",
					"BASE_URL_ENV": "production"
				},
				"define": {
					"CUSTOM-CONST": true
				}
			}
		}
	}
}

3.2 根目录新建vue.config.js

  • 利用child_process.exec执行子进程,运行npx tailwindcss -i ./tailwind-input.css -o ./static/css/tailwind.css --watch,
  • 问题也就出现在这,child_process.exec默认用的是/bin/sh执行,虽然可以配置修改成{shell: '/bin/bash'}或者{shell: '/bin/zsh'},
  • 但是tailwindcss每次都报错,因为HbuilderX执行vue.config.js里采用的nodejs版本是v12,通过console.log(process.version)可以看到nodejs当前版本
  • 为什么它这里采用的是nodejs v12版本,暂不清楚,但本地zsh,执行node -v,是v16版本
  • nodejs v12版本执行tailwindcss编译,Npx tailwindcss results in "Unexpected Token ."
  • 根据提示,解决办法就是切换当前nodejs版本为v16

一次uniapp HbuilderX创建的小程序项目中使用tailwindcss折腾记录

const {exec} = require('child_process');

console.log("process.env.UNI_SCRIPT:", process.env.UNI_SCRIPT);
console.log("process.env.NODE_ENV:", process.env.NODE_ENV);
console.log("当前nodejs版本", process.version);

const isDev = process.env.NODE_ENV === 'development';

exec(
	isDev ? 'npm run tailwind:dev' : '"npm run tailwind:prod',
	{ cwd: __dirname, shell: "/bin/bash", },
	(error, stdout, stderr) => {  
		if (error) {  
			console.error('[tailwindcss error]', error);
			console.error("error.stderr:", stderr);
		} 
		
		isDev ? console.log(`[tailwindcss stdout]: ${stdout}`)
			: console.log('[tailwindcss] 生产环境打包完成');
	}
);

module.exports = {};

报错信息如下:

[tailwindcss error] Error: Command failed: npm run tailwind:dev
14:38:24.610 Unexpected token '.'
14:38:24.622 npm ERR! code ELIFECYCLE
14:38:24.623 npm ERR! errno 1
14:38:24.638 npm ERR! @ tailwind:dev: `npx tailwindcss -i ./tailwind-input.css -o ./static/css/tailwind.css --watch`
14:38:24.639 npm ERR! Exit status 1
14:38:24.652 npm ERR! 
14:38:24.668 npm ERR! Failed at the @ tailwind:dev script.
14:38:24.669 npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
14:38:24.686 npm ERR! A complete log of this run can be found in:
14:38:24.702 npm ERR!     /Users/xxx/.npm/_logs/2023-07-18T06_38_23_626Z-debug.log
14:38:24.719     at ChildProcess.exithandler (child_process.js:308:12)
14:38:24.740     at ChildProcess.emit (events.js:314:20)
14:38:24.757     at maybeClose (internal/child_process.js:1022:16)
14:38:24.774     at Socket.<anonymous> (internal/child_process.js:444:11)
14:38:24.790     at Socket.emit (events.js:314:20)
14:38:24.791     at Pipe.<anonymous> (net.js:675:12) {
14:38:24.807   killed: false,
14:38:24.810   code: 1,
14:38:24.825   signal: null,
14:38:24.840   cmd: 'npm run tailwind:dev'
14:38:24.841 }

3.3 在vue.config.js中切换nodejs版本

3.3.1 原本想直接nvm use v16.14.2,但是nvm命令找不到

console.log("当前nodejs版本", process.version);

exec("nvm use 16.14.2", { cwd: __dirname, shell: "/bin/bash", }, error => {
	console.error(error);
})

报错信息如下:

当前nodejs版本 v12.22.1
Error: Command failed: nvm use 16.14.2
14:49:05.163 /bin/bash: nvm: command not found
14:49:05.172     at ChildProcess.exithandler (child_process.js:308:12)
14:49:05.172     at ChildProcess.emit (events.js:314:20)
14:49:05.184     at maybeClose (internal/child_process.js:1022:16)
14:49:05.185     at Socket.<anonymous> (internal/child_process.js:444:11)
14:49:05.197     at Socket.emit (events.js:314:20)
14:49:05.197     at Pipe.<anonymous> (net.js:675:12) {
14:49:05.209   killed: false,
14:49:05.212   code: 127,
14:49:05.226   signal: null,
14:49:05.244   cmd: 'nvm use 16.14.2'
14:49:05.260 }

3.3.2 shelljs登场

这破烂child_process.exec,不折腾了,用shelljs

  • vue.config.js
const shell = require('shelljs');
const isDev = process.env.NODE_ENV === 'development';

shell.cd(__dirname);
		
	isDev 
		? shell.exec('bash ./tailwindcss.sh development', {shell: "/bin/bash",async:true})
		: shell.exec('bash ./tailwindcss.sh production', {shell: "/bin/bash",async:true});
		
module.exports = {};
  • 根目录新建tailwindcss.sh
#!/bin/bash

CURRENT_NODE_ENV=$1
DEVELOPMENT="development"

echo "当前NODE_ENV:"
echo $CURRENT_NODE_ENV

# 切换到当前目录
cd $(dirname $0);

echo "当前目录:"
pwd

{ # try
 
    source ~/.nvm/nvm.sh;
    
    #切换nodejs版本
    nvm use 16.14.2
	
	echo "切换后nodejs版本:"
	node -v
    
    if [ $CURRENT_NODE_ENV == $DEVELOPMENT ]
    then
    	npm run tailwind:dev
    else
    	npm run tailwind:prod
    fi
} || { # catch
    echo "tailwindcss 执行错误,请检查";
}

sleep 3
  • 当执行Hbuilderx顶部菜单运行 - 本地开发-测试环境接口

此时nodejs版本倒是正确切换了,tailwindcss也正确执行了,但是shell命令执行阻塞了,停留在那不继续往下执行了(HbuilderX不继续编译vue为小程序了)

正在编译中...
15:19:40.696 process.env.UNI_SCRIPT: dev:mp-weixin:dev
15:19:40.705 process.env.NODE_ENV: development
15:19:40.706 isDev: true
15:19:40.716 当前nodejs版本 v12.22.1
15:19:40.766 当前NODE_ENV:
15:19:40.766 development
15:19:43.041 Now using node v16.14.2 (npm v8.5.0)
15:19:43.051 切换后nodejs版本:
15:19:43.068 v16.14.2
15:19:43.450 > tailwind:dev
15:19:43.461 > npx tailwindcss -i ./tailwind-input.css -o ./static/css/tailwind.css --watch
15:19:44.696 Rebuilding...
15:19:45.038 Done in 388ms.

3.3.3 shell执行阻塞解决

两种方法:

  • 1 shell.exec('bash ./tailwindcss.sh development', {shell: "/bin/bash", async:true})

  • 2 shell.exec('bash ./tailwindcss.sh development &', {shell: "/bin/bash"})

3.3.4 又遇坎坷,uview-ui报错

好不容易让tailwindcss正常编译,但是项目中的uview ui又报错了,心累

报错信息如下:

Module parse failed: Unexpected token (224:64)
15:28:17.218 File was processed with these loaders:
15:28:17.234  * ./node_modules/babel-loader/lib/index.js
15:28:17.287  * ./node_modules/@dcloudio/vue-cli-plugin-uni/packages/webpack-preprocess-loader/index.js
15:28:17.319  * ./node_modules/@dcloudio/webpack-uni-mp-loader/lib/script.js
15:28:17.321  * ./node_modules/@dcloudio/vue-cli-plugin-uni/packages/vue-loader/lib/index.js
15:28:17.405  * ./node_modules/@dcloudio/webpack-uni-mp-loader/lib/style.js
15:28:17.430 You may need an additional loader to handle the result of these loaders.
15:28:17.475 |         const grandChild = child.$children; // 判断如果在需要重新初始化的组件数组中名中,并且存在init方法的话,则执行
15:28:17.476 | 
15:28:17.503 >         if (names.includes(child.$options.name) && typeof child?.init === 'function') {
15:28:17.504 |           // 需要进行一定的延时,因为初始化页面需要时间
15:28:17.530 |           uni.$u.sleep(50).then(() => {
15:28:17.531 Module parse failed: Unexpected token (3:49)
15:28:17.551 File was processed with these loaders:
15:28:17.551  * ./node_modules/babel-loader/lib/index.js
15:28:17.663  * ./node_modules/@dcloudio/vue-cli-plugin-uni/packages/webpack-preprocess-loader/index.js
15:28:17.694 You may need an additional loader to handle the result of these loaders.
15:28:17.695 | // 看到此报错,是因为没有配置vue.config.js的【transpileDependencies】,详见:https://www.uviewui.com/components/npmSetting.html#_5-cli模式额外配置
15:28:17.714 | const pleaseSetTranspileDependencies = {},
15:28:17.715 >       babelTest = pleaseSetTranspileDependencies?.test; // 引入全局mixin
15:28:17.740 | 
15:28:17.891 | import mixin from './libs/mixin/mixin.js'; // 小程序特有的mixin

根据提示,是要在vue.config.js中添加transpileDependencies配置:

module.exports = {
	transpileDependencies: ['uview-ui']
};
  • 修改后,继续执行,还是同样的报错,根据uview ui文档,Hbuilderx方式安装无需再vue.config.js中添加transpileDependencies配置
  • 删掉刚才的transpileDependencies: ['uview-ui']配置
  • 难道要用uview ui提供的npm方式安装吗?试了一遍,还是同样的报错,默默的撤回了修改

3.3.5 uview-ui报错的分析解决

其实就是,tailwindcss的编译不能和HbuilderX编译小程序在同一时间执行,要错开

  1. 可以新开一个shell窗口执行tailwindcss编译
  2. 或者在子进程中执行tailwindcss编译
  3. 或者拿到HbuilderX编译小程序完毕后打开微信开发者工具的回调,然后再另执行tailwindcss编译
  4. setTimeout大法
const shell = require('shelljs');
const isDev = process.env.NODE_ENV === 'development';

setTimeout(() => {
	shell.cd(__dirname);
			
		isDev 
			? shell.exec('bash ./tailwindcss.sh development &', {shell: "/bin/bash"})
			: shell.exec('bash ./tailwindcss.sh production', {shell: "/bin/bash",async:true});
}, 30000); // 30s后执行
		
module.exports = {};

可以看到,HbuilderX先进行了vue编译成小程序,然后tailwindcss进行监听编译,先后顺序错开,执行ok

修改文件也可以生效

process.env.UNI_SCRIPT: dev:mp-weixin:dev
15:43:08.945 process.env.NODE_ENV: development
15:43:08.958 isDev: true
15:43:08.973 当前nodejs版本 v12.22.1
15:43:10.582 ​Browserslist: caniuse-lite is outdated. Please run:
15:43:10.593 npx browserslist@latest --update-db​
15:43:27.934 项目 'xxx' 编译成功。前端运行日志,请另行在小程序开发工具的控制台查看。
15:43:27.951 正在启动微信开发者工具...
15:43:28.941 [微信小程序开发者工具] - initialize
15:43:28.952 [微信小程序开发者工具]
15:43:28.953 [微信小程序开发者工具]
15:43:29.029 [微信小程序开发者工具] ✔ IDE server has started, listening on http://127.0.0.1:59243
15:43:29.075 [微信小程序开发者工具]
15:43:29.092 [微信小程序开发者工具] - open IDE
15:43:29.169 [微信小程序开发者工具]
15:43:29.169 [微信小程序开发者工具]
15:43:29.895 [微信小程序开发者工具] ✔ open IDE
15:43:29.910 [微信小程序开发者工具]
15:43:29.911 微信开发者工具已启动,在HBuilderX中修改文件并保存,会自动刷新微信模拟器
15:43:29.932 注:
15:43:29.961 1. 可以通过微信开发者工具切换pages.json中condition配置的页面,或者关闭微信开发者工具,然后再从HBuilderX中启动指定页面
15:43:29.992 2. 如果出现微信开发者工具启动后白屏的问题,检查是否启动多个微信开发者工具,如果是则关闭所有打开的微信开发者工具,然后再重新运行
15:43:30.024 3. 运行模式下不压缩代码且含有sourcemap,体积较大;若要正式发布,请点击发行菜单进行发布
15:43:39.028 当前NODE_ENV:
15:43:39.028 development
15:43:42.386 Now using node v16.14.2 (npm v8.5.0)
15:43:42.401 切换后nodejs版本:
15:43:42.425 v16.14.2
15:43:43.144 > tailwind:dev
15:43:43.162 > npx tailwindcss -i ./tailwind-input.css -o ./static/css/tailwind.css --watch
正在差量编译...
15:45:29.429 项目 'xxx' 编译成功。前端运行日志,请另行在小程序开发工具的控制台查看。

setTimeout大法虽好,但是不优雅,30s不保证HbuilderX编译vue能结束

3.3.6 最终版本-优雅解决uview-ui的报错

  • 这里还是用到了child_process.exec子进程执行,境泽真香定律!
  • child_process.exec执行shell可能会遇上Permission denied无权限执行, chmod(u+x, /xx.sh)解决

一次uniapp HbuilderX创建的小程序项目中使用tailwindcss折腾记录

const path = require("path");
const shell = require('shelljs');
const {exec} = require('child_process');

console.log("process.env.UNI_SCRIPT:", process.env.UNI_SCRIPT);
console.log("process.env.NODE_ENV:", process.env.NODE_ENV);

const isDev = process.env.NODE_ENV === 'development';

console.log("isDev:", isDev);
console.log("当前nodejs版本", process.version);

function executeTailWindCssSh() {
	// https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback
	exec(
		isDev ? '"./tailwindcss.sh" development' : '"./tailwindcss.sh" production',
		{ cwd: __dirname, shell: "/bin/bash", },
		(error, stdout, stderr) => {
			if (error) {  
				console.error('[tailwindcss error]', error);
				console.error("error.stderr:", stderr);
				
				if(stderr && stderr.includes("Permission denied")) {
					// 给当前user增加运行文件权限
					shell.chmod("u+x", path.resolve(__dirname, './tailwindcss.sh'));
					
					executeTailWindCssSh();
				}
			} 
			
			isDev ? console.log(`[tailwindcss stdout]: ${stdout}`)
				: console.log('[tailwindcss] 生产环境打包完成');
		}
	);
}

executeTailWindCssSh();

module.exports = {};

4.参考资料

  1. Hbuilder创建的uniapp工程,使用tailwindcss最优雅的方式

  2. shelljs

  3. child_process.exec

  4. Tailwind CLI