《记一忘三二》前端构建工具学习

Scripts构建

sass编译

首先需要将./scss/style.scss转为./dist/style.css

bash 复制代码
# 安装sass编译工具
pnpm i sass -D
bash 复制代码
# 使用sass编译工具
npx sass ./scss/style.scss ./dist/style.css

但这样在执行编译命令时,需要记住目录、配置等编译参数

json 复制代码
{
    "scripts": {
        "build": "sass ./scss/style.scss ./dist/style.css"
    }, 
    "devDependencies": {
        "sass": "^1.85.1"
    }
}

配置scripts能够给长命令 取一个命令别名,这样只需要执行这个命令别名就可以了

bash 复制代码
# 执行scripts的命令
npx run build

browser-sync服务

开启web服务器

bash 复制代码
# 安装browser-sync
npm i browser-sync -D
bash 复制代码
#  启动web服务器
npx browser-sync ./

命令在scripts中配置命令别名

json 复制代码
{
    "scripts": {
        "build": "sass ./scss/style.scss ./dist/style.css",
        "browser": "browser-sync ./"
    },
    "devDependencies": {
        "browser-sync": "^3.0.3",
        "sass": "^1.85.1"
    }
}

**&&**串行

build命令browser命令都是需要单独执行的,但是sass编译是项目启动的前提,也就是说在执行browser命令时,都需要提前执行build命令

通过添加serve命令,使用**&&实现 串行**build命令browser命令

json 复制代码
{
    "scripts": {
        "build": "sass ./scss/style.scss ./dist/style.css",
         "browser": "browser-sync ./ --files \"dist/*.css\"",
        "serve": "npm run build && npm run browser"
    },
    "devDependencies": {
        "browser-sync": "^3.0.3",
        "sass": "^1.85.1"
    }
}

sass编译添加--watch

json 复制代码
{
    "scripts": {
        "build": "sass ./scss/style.scss ./dist/style.css --watch",
        "browser": "browser-sync ./",
        "serve": "npm run build && npm run browser"
    },
    "devDependencies": {
        "browser-sync": "^3.0.3",
        "sass": "^1.85.1"
    }
}

单独执行build命令并没有发生问题,并且scss文件也能实现监听并且改变

但是去执行serve命令,会发现build命令之后,控制台就停顿了,后续未执行browser命令

npm-run-all

因为--watch需要单独开启一个控制台, build命令browser命令串行显然是行不通的

bash 复制代码
npm install npm-run-all --save-dev
json 复制代码
{
    "scripts": {
        "build": "sass ./scss/style.scss ./dist/style.css --watch",
        "browser": "browser-sync ./ --files \"dist/*.css\"",
        "serve": "run-p  build browser"
    },
    "devDependencies": {
        "browser-sync": "^3.0.3",
        "npm-run-all": "^4.1.5",
        "sass": "^1.85.1"
    }
}

run-p能够并行 执行build命令browser命令

Grunt工作流

基础使用

bash 复制代码
# 安装grunt
npm i grunt -D

根目录创建gruntfile.js文件

js 复制代码
module.exports = (grunt) => {
    grunt.registerTask("task", () => {
        console.log("task")
    })
}
bash 复制代码
# 执行 task 任务
npx grunt task

定义任务

grunt.registerTask(任务名称, 任务函数)

同步任务

js 复制代码
module.exports = (grunt) => {
    grunt.registerTask("task", () => {
        console.log("task")
    })
}
bash 复制代码
# 执行 task 任务
npx grunt task

异步任务

js 复制代码
grunt.registerTask("async-task", function () {
    const done = this.async()
    setTimeout(() => {
        console.log("async-task")
        done()
    }, 1000)
})

done函数被调用,表示异步任务执行完毕

注:使用this关键生成done函数,所以不能使用箭头函数

bash 复制代码
# 执行 task 任务
npx grunt async-task

默认任务

js 复制代码
module.exports = (grunt) => {
    grunt.registerTask("task", () => {
        console.log("task")
    })

    grunt.registerTask("default", ["task"])
}
bash 复制代码
# 执行默认任务,会自动执行第二参数的全部任务名称
npx grunt 

标记任务失败

同步任务

js 复制代码
module.exports = (grunt) => {
  grunt.registerTask('error-task', () => {
    return false
  })
}
bash 复制代码
# 执行 error-task 任务
npx grunt error-task

异步任务

js 复制代码
module.exports = (grunt) => {
  grunt.registerTask('async-error-task', function () {
    const done = this.async()
    setTimeout(() => {
      done(false)
    }, 1000)
  })
}
bash 复制代码
# 执行 async-error-task 任务
npx grunt async-error-task

多个任务

js 复制代码
module.exports = (grunt) => {
  grunt.registerTask('error-task-1', () => {
    console.log('error-task-1')
    return false
  })
  grunt.registerTask('error-task-2', () => {
    console.log('error-task-2')
  })

  grunt.registerTask('default', ['error-task-1', 'error-task-2'])
}
bash 复制代码
npx grun

当前任务执行失败还会影响后续任务执行

js 复制代码
npx grun --force

--force能够忽略执行失败的任务,后续的任务会照常执行

执行方式

中途有任务标记失败,就会终止这次执行

js 复制代码
module.exports = (grunt) => {
  // 定义同步任务 task-1
  grunt.registerTask('task-1', () => {
    console.log('task-1')
  })

  // 定义同步任务 task-2
  grunt.registerTask('task-2', function () {
    const done = this.async()
    setTimeout(() => {
      console.log('task-2')
      done()
    }, 1000)
  })

  // 定义同步任务 task-3
  grunt.registerTask('task-3', () => {
    console.log('task-3')
  })

  // 定义任务队列
  grunt.registerTask('default', ['task-1', 'task-2', 'task-3'])
}

同步串行

js 复制代码
module.exports = (grunt) => {
  grunt.registerTask('hello', () => {
    console.log('Hello, Grunt!')
  })

  grunt.registerTask('world', () => {
    console.log('Hello, World!')
  })

  grunt.registerTask('say', () => {
    console.log('Hello, say!')
  })

  grunt.registerTask('parallel', ['hello', 'world', 'say'])
}

异步串行

异步任务会等待上一个任务执行完毕

js 复制代码
module.exports = (grunt) => {
  grunt.registerTask('async-task-1', function () {
    const done = this.async()
    setTimeout(() => {
      console.log('async-task-1')
      done()
    }, 1000)
  })

  grunt.registerTask('async-task-2', function () {
    const done = this.async()
    setTimeout(() => {
      console.log('async-task-2')
      done()
    }, 1000)
  })

  grunt.registerTask('async-task-3', function () {
    const done = this.async()
    setTimeout(() => {
      console.log('async-task-3')
      done()
    }, 1000)
  })

  grunt.registerTask('series', ['async-task-1', 'async-task-2', 'async-task-3'])
}

配置参数

options

this.options()获取的配置参数,只要在initConfig配置的参数都可以按照路径进行访问

js 复制代码
module.exports = (grunt) => {
  grunt.initConfig({
    build: {
      options: {
        soureMap: true,
      },
    },
  })

  grunt.registerTask('build', function () {
    const options = this.options()

    console.log(options)
  })
}

grunt.initConfig数据

grunt.initConfig配置的数据,可通过grunt.config('具体路径')获取数据

js 复制代码
module.exports = (grunt) => {
  grunt.initConfig({
    js: './src/js/index.js',
    build: {
      css: './src/css/index.css',
    },
  })

  grunt.registerTask('build', () => {
    const js = grunt.config('js')
    const css = grunt.config('build.css')

    console.log(css, js)
  })
}

注: grunt.config能够获取options数据

多目标任务

js 复制代码
module.exports = (grunt) => {
  grunt.initConfig({
    build: {
      options: {
        soureMap: true,
      },
      css: {
        options: {
          soureMap: false,
        },
        src: './css/index.css',
        dist: './css/index.css',
      },
      js: {
        src: './js/index.js',
        dist: './js/index.js',
      },
    },
  })

  grunt.registerMultiTask('build', function () {
    const options = this.options()
    const target = this.target
    const data = this.data

    console.log(target, options, data)
  })
}

一个任务有多个目标,也可以说一个目标下面的子任务

在执行build任务时,会执行cssjs两个子任务

bash 复制代码
# 执行 build 主任务
npx grunt build

并且还可以单独子任务

bash 复制代码
# 执行 build:css 子任务
npx grunt build:css
# 执行 build:js 子任务
npx grunt build:js

插件使用

grunt-contrib-clean

bash 复制代码
# 安装 grunt-contrib-clean
pnpm  i grunt-contrib-clean -D

加载grunt-contrib-clean插件,并设置任务目标

js 复制代码
module.exports = (grunt) => {
  grunt.initConfig({
    // 设置 grunt-contrib-clean 的目标
    clean: {
      dist: {
        src: ['./dist/**'],
      },
    },
  })

  // 加载 grunt-contrib-clean 插件
  grunt.loadNpmTasks('grunt-contrib-clean')
}
bash 复制代码
# 执行 clean 任务
npx grunt clean

注:一般情况下,grunt的插件名称是grunt-contrib-(插件任务名称)

综合示例

bash 复制代码
# 安装依赖
npm install grunt-contrib-clean grunt-contrib-uglify @babel/core @babel/preset-env grunt grunt-babel grunt-contrib-watch -D
js 复制代码
module.exports = (grunt) => {
  grunt.initConfig({
    clean: {
      dist: {
        src: ['./dist/**'],
      },
    },
    uglify: {
      dist: {
        files: {
          './dist/index.js': './dist/index.js',
        },
      },
    },
    babel: {
      options: {
        presets: ['@babel/preset-env'],
      },
      dist: {
        files: {
          './dist/index.js': './src/index.js',
        },
      },
    },
    watch: {
      scripts: {
        files: ['src/**/*.js'],
        tasks: ['babel', 'uglify'],
        options: {
          spawn: false,
        },
      },
    },
  })

  grunt.loadNpmTasks('grunt-babel')
  grunt.loadNpmTasks('grunt-contrib-clean')
  grunt.loadNpmTasks('grunt-contrib-uglify')
  grunt.loadNpmTasks('grunt-contrib-watch')
  grunt.registerTask('default', ['clean', 'babel', 'uglify', 'watch'])
}

Gulp工作流

基础使用

bash 复制代码
npm i gulp -D

根目录创建gulpfile.js文件

js 复制代码
exports.world = (done) => {
  console.log('world')
  done()
}

exports.asyncTask = (done) => {
  setTimeout(() => {
    console.log('asyncTask')
    done()
  })
}

exports.default = (done) => {
  console.log('default')
  done()
}
bash 复制代码
# 执行 world 任务
npx gulp world
# 执行 asyncTask 任务
npx gulp asyncTask
# 执行 default 任务,也就是默认任务
npx gulp

定义任务

任何任务都需要标记结束,通常使用done

默认任务

js 复制代码
exports.default = (done) => {
  console.log('default')
  done()
}

同步任务

js 复制代码
exports.world = (done) => {
  console.log('world')
  done()
}

异步任务

done
js 复制代码
exports.asyncTask = (done) => {
  setTimeout(() => {
    console.log('asyncTask')
    done()
  })
}
Promise
js 复制代码
exports.asyncTask = () => {
  console.log('asyncTask')
  return Promise.resolve()
}
async
JS 复制代码
exports.asyncTask = async () => {
  console.log('asyncTask')
}
on监听
js 复制代码
const {
  src,
  dest,
} = require('gulp')

exports.copy_src = () => {
  return src('src/**').pipe(dest('dist'))
}

自动监听end事件进行任务结束判断

js 复制代码
const {
  src,
  dest,
} = require('gulp')

exports.copy_src = (done) => {
  src('src/**').pipe(dest('dist')).on('end', done)
}

标记任务失败

done
js 复制代码
exports.error_task = (done) => {
  done(new Error('error-task failed'))
}

exports.async_error_task = (done) => {
  setTimeout(() => {
    done(new Error('async-error-task failed'))
  }, 1000)
}
Promise
js 复制代码
exports.error_task = () => {
  return Promise.reject(new Error('error-task failed'))
}
async
js 复制代码
exports.error_task = async () => {
  throw new Error('error-task failed')
}

执行方式

异步串行

js 复制代码
const { series } = require('gulp')

function async_task_1(done) {
  setTimeout(() => {
    console.log('async task 1')
    done()
  }, 1000)
}

function async_task_2(done) {
  setTimeout(() => {
    console.log('async task 2')
    done()
  }, 1000)
}

function async_task_3(done) {
  setTimeout(() => {
    console.log('async task 3')
    done()
  }, 1000)
}

exports.series = series(async_task_1, async_task_2, async_task_3)

异步并行

js 复制代码
const { parallel } = require('gulp')

function async_task_1(done) {
  setTimeout(() => {
    console.log('async task 1')
    done()
  }, 1000)
}

function async_task_2(done) {
  setTimeout(() => {
    console.log('async task 2')
    done()
  }, 1000)
}

function async_task_3(done) {
  setTimeout(() => {
    console.log('async task 3')
    done()
  }, 1000)
}

exports.parallel = parallel(async_task_1, async_task_2, async_task_3)

同步串行

js 复制代码
const { series } = require('gulp')

function task_1(done) {
  console.log('async task 1')
  done()
}

function task_2(done) {
  console.log('async task 2')
  done()
}

function task_3(done) {
  console.log('async task 3')
  done()
}

exports.series = series(task_1, task_2, task_3)

异步并行

js 复制代码
const { parallel } = require('gulp')

function task_1(done) {
  console.log('async task 1')
  done()
}

function task_2(done) {
  console.log('async task 2')
  done()
}

function task_3(done) {
  console.log('async task 3')
  done()
}

exports.parallel = parallel(task_1, task_2, task_3)

Webpack

devtool

当浏览器加载并执行一个包含sourceMappingURL注释的JavaScript文件时,会自动根据该注释中指定的目录地址加载对应的映射文件。浏览器将根据加载到的文件内容,生成源文件现文件之间的映射信息,从而实现源代码的调试功能

会根据不同的devtool生成不同的sourceMappingURL注释

方式

false

不生成映射注释

eval
js 复制代码
//# sourceURL=webpack://my-webpack/./src/main.js?

浏览器不会加载映射文件,根据源文件 的内容在src/main.js位置生成一份一样的文件,映射关系介绍行对行

source-map
js 复制代码
//# sourceMappingURL=bundle.js.map

浏览器加载映射文件bundle.js.map,生成映射关系源文件

映射文件

json 复制代码
{
    "version": 3,
    "file": "bundle.js",
    "mappings": ";;;;;;;;;;;;;;;;;AAAA,.....CAACT,gDAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,C",
    "sources": [
        "webpack://my-webpack/./src/utils/math.js",
        "webpack://my-webpack/webpack/bootstrap",
        "webpack://my-webpack/webpack/runtime/define property getters",
        "webpack://my-webpack/webpack/runtime/hasOwnProperty shorthand",
        "webpack://my-webpack/webpack/runtime/make namespace object",
        "webpack://my-webpack/./src/main.js"
    ],
    "sourcesContent": [
        "const sum = (a, b) => a + b;\r\n\r\nconst subtract = (a, b) => a - b;\r\n\r\nconst ...... \r\nconsole.log(sum(1, 2));"
    ],
    "names": [
        "sum",
        "a",
        "subtract",
        "b",
        "address"
    ],
    "sourceRoot": ""
}
  • version: 指定源码映射的版本,这里是第 3 版。第 2 版比第 1 版生成的映射文件大小减少%,第 3 版比第 2 版减少%
  • file : 构建后的文件名称 bundle.js
  • mappings: 包含了实际的映射关系,这是一个 Base64 编码的字符串,用于描述源文件和构建后文件之间的对应关系。这个字符串的具体含义需要通过解码和解析才能理解
  • sources : 包含了源文件的路径列表。这里列出了多个源文件,例如 src/utils/math.jssrc/main.js 等,这些文件可能通过 Webpack 等工具打包生成了 bundle.js
  • sourcesContent: 包含了源文件的内容
  • names: 包含了源文件中出现的变量和函数名称
  • sourceRoot:表示指定源文件的根目录
相关推荐
ywf121529 分钟前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭37 分钟前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf7 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特7 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷7 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
mengchanmian8 小时前
前端node常用配置
前端
华洛8 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
xkxnq8 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js
A黄俊辉A9 小时前
vue css中 :global的使用
前端·javascript·vue.js
小码哥_常9 小时前
被EdgeToEdge适配折磨疯了,谁懂!
前端