当下编译 less 的方式有很多如:
- 利用代码编辑器插件编译, 如vscode编辑器 等, 这种适合人数少的团队, 通过简单约定
- 利用项目所使用的框架, 如项目中用到了 webpack / rollup / vite 这些工具, 可以直接配置使用
- 如果项目没有用到webpack vite 等大型打包工具, 可以使用 gulp 这种轻量的工具 编译 less
gulp 利用 gulp-less 插件编译 less 文件到 css , 需要用到 nodejs 的支持,
如果没有 nodejs 请先去安装nodejs
本文依据 node 版本如下:
$ node -v
v14.17.0
项目基本目录结构示意
项目
| public
|----| less
|----|----|common.less
|----|----|page
|----|----|----|home.less
|----| css
|----|----|common.css
|----|----|home.css
| gulpfile.js
| package.json
| package-lock.json
初始化package.json
在项目根目录, 寻找有没有 package.json
文件
如果没有这个文件, 则使用 npm init
生成 package.json
文件
安装依赖包
执行如下命令,安装依赖包,
如果已经安装了部分依赖, 可以不安装
npm install gulp gulp-clean gulp-clean-css gulp-less gulp-rename less-plugin-autoprefix -D
本文安装依赖版本如下:
"devDependencies": {
"gulp": "^4.0.2",
"gulp-clean": "^0.4.0",
"gulp-clean-css": "^4.3.0",
"gulp-less": "^5.0.0",
"gulp-rename": "^2.0.0"
"less-plugin-autoprefix": "^2.0.0"
},
"gulp-clean": 用来清空文件夹
“gulp-clean-css”: 压缩css文件
“gulp-less”: 编译less文件
“gulp-rename”: 重命名输出文件
“less-plugin-autoprefix”: 为css自动添加兼容写法
添加 npm 命令
在项目根目录, 可以直接执行 gulp XXX ,
但是为了方便记忆, 和 使用, 我选择放到 scripts 中,
"scripts": {
"watch": "gulp",
"build": "gulp build",
"clean": "gulp clean"
},
创建 gulp 配置文件
在项目根目录, 创建 gulpfile.js 文件, 用来编写gulp配置文件.
配置项
ROOT_DIR Less 和 css 的共同目录 public
OUTPUT_LESS Less 文件输出目录
INPUT_LESS Less 文件输入目录, 这个是一个数组
SOURCEMAPS 是否开启 sourcemaps
// 配置
const ROOT_DIR = "./public";
// Less 文件输出目录
const OUTPUT_LESS = `${ROOT_DIR}/css/`;
// Less 文件输入目录
const INPUT_LESS = [
`${ROOT_DIR}/less/`,
`${ROOT_DIR}/less/page/`,
]
// 是否开启 sourcemaps
const SOURCEMAPS = true;
gulpfile.js 全部内容
const fs = require('fs');
const { src,dest, watch, series, parallel } = require('gulp');
const less = require('gulp-less');
const minifycss = require('gulp-clean-css')
const clean = require('gulp-clean')
const rename = require('gulp-rename')
const LessAutoprefix = require('less-plugin-autoprefix');
const autoprefix = new LessAutoprefix({ browsers: ['last 2 versions'] });
// const path = require('path');
// 配置
const ROOT_DIR = "./public";
// Less 文件输出目录
const OUTPUT_LESS = `${ROOT_DIR}/css/`;
// Less 文件输入目录
const INPUT_LESS = [
`${ROOT_DIR}/less/`,
`${ROOT_DIR}/less/page/`,
]
// 是否开启 sourcemaps
const SOURCEMAPS = true;
function existsDir(file){
file = file.replace(/\/$/,"");
const stats = fs.statSync(file);
if(!stats.isDirectory()) {
throw new Error(`${file} 不是一个文件夹`)
};
}
// 检验输出文件夹是否存在
existsDir(OUTPUT_LESS)
// 清空输出文件夹
function cleanTasks(cb) {
src(`${OUTPUT_LESS}`, {read: false})
.pipe(clean());
cb();
}
// less 编译流程
function lessTask(srcpath, cb) {
console.log(srcpath)
src(srcpath, { sourcemaps: SOURCEMAPS })
.pipe(less({ plugins: [autoprefix] }))
.pipe(rename(function (path) {
const dirname = OUTPUT_LESS.replace(new RegExp(ROOT_DIR + '\/' ),'');
return {
dirname: dirname,
basename: path.basename,
extname: ".css"
};
}))
.pipe(minifycss())
.pipe(dest(`${ROOT_DIR}`, { sourcemaps: SOURCEMAPS }));
cb && cb();
}
function doneTask(cb) {
console.log(`任务完成`);
cb();
}
// 获取less目录下所有less文件
function getLessFile(rootPath){
let files = []
try {
const fileList = fs.readdirSync(rootPath) || [];
fileList.forEach(function(file){
const filePath = rootPath + file
const stats = fs.statSync(filePath)
if(stats.isDirectory()) return;
if(/\.less$/.test(file)) {
files.push(filePath);
}
})
} catch (error) {
console.log("读取文件目录失败:",error)
}
return files
}
// 每一个less 文件生成一个less编译任务, 目的是所有less文件,异步编译, 提高编译速度
function lessParallelTasks(){
const fileList = INPUT_LESS.reduce((files,dir) => files.concat(getLessFile(dir)), []);
return fileList.map(function(filePath){
return cb => lessTask(filePath, cb);
});
}
// 观察指定文件的变化, 然后编译这个文件
function watchTasks() {
const lessList = INPUT_LESS.map(dir => `${dir}*.less`);
watch(lessList).on('all', function(eventName, stats) {
lessTask(stats)
console.log(`[${eventName}] ${stats} 编译完成`);
});
// 其他一些基础less, 修改这些需要全部编译一次
watch([`${ROOT_DIR}/less/lib/*.less`], series( parallel(lessParallelTasks()), doneTask));
}
exports.build = series(cleanTasks, parallel(lessParallelTasks()), doneTask);
exports.clean = series(cleanTasks);
exports.watch = watchTasks;
exports.default = watchTasks;