美文网首页
JavaScript工程化(学习笔记)

JavaScript工程化(学习笔记)

作者: A_走在冷风中 | 来源:发表于2021-09-02 20:50 被阅读0次

工程化的价值

工程化是一样开发规范,一切以提高效率、降低成本、质量保证为目的手段都可称为'工程化'
在我日常开发过程中常常会遇到遇到问题
如:

  • 语法新特性的兼容性问题
  • 模块化兼容性问题
  • 多人协作开发代码风格不统一
  • 过于依赖后端接口数据
  • 自动化部署的实现等问题
  • 这些问题就需要工程化去解决
利用工程化主要能解决的问题有:
  • 传统语言或语法的弊端
  • 无法使用组件化/模块化
  • 重复的机械式工作
  • 代码风格统一、质量保证
  • 依赖后端服务接口支持
  • 整体依赖后端项目
  • 自动化部署的实现

脚手架的意义

提供项目的规范和约定,如:
1.相同的组织结构
2.相同的开发范式
3.相同的模块依赖
4.相同的工具配置
4.相同的基础代码

自定义yeoman脚手架

1.安装脚手架工具

yarn add yo -g

验证是否安装成功:yo --version

2.安装yo成功后, 生成 generator

yarn add generator-generator -g

3.创建文件夹 mkdir generator-my-vue

4.在文件夹下运行yarn init --yes,初始化package.json

5.安装yeoman-generator

yarn add yeoman-generator --dev

6.创建入口文件genrator/app/index.js

const Generator = require('yoman-generator')

module.exports = class extends Generator {
  prompting() {
    // Yeoman 在询问用户环节会自动调用此方法
    // 在此方法中可以调用父类的 prompt() 方法发出对用户的命令行询问
    return this.prompt([
      {
        type: 'input',
        name: 'name',
        message: 'Your project name',
        default: this.appname, // appname 为项目生成目录名称
      },
    ]).then((answers) => {
      // answers => { name: 'user input value' }
      this.answers = answers
    })
  }
  writing() {
    // 把每一个文件都通过模板转换到目标路径

    const templates = [
      '.browserslistrc',
      '.editorconfig',
      '.env.development',
      '.env.production',
      '.eslintrc.js',
      '.gitignore',
      'babel.config.js',
      'package.json',
      'postcss.config.js',
      'README.md',
      'public/favicon.ico',
      'public/index.html',
      'src/App.vue',
      'src/main.js',
      'src/router.js',
      'src/assets/logo.png',
      'src/components/HelloWorld.vue',
      'src/store/actions.js',
      'src/store/getters.js',
      'src/store/index.js',
      'src/store/mutations.js',
      'src/store/state.js',
      'src/utils/request.js',
      'src/views/About.vue',
      'src/views/Home.vue',
    ]

    //循环模板,创建目录
    templates.forEach((item) => {
      // item => 每个文件路径
      this.fs.copyTpl(this.templatePath(item), this.destinationPath(item), this.answers)
    })
  }
}

7.generator下创建 templates 里面放入要生成的模板
注:模板文件与index.js中的 templates 对应

  1. 运行 yarn link

9.创建新的文件夹,在文件下运行 yo my-vue

10.文件自动生成

nodejs自定义脚手架

  • 创建文件夹 my-sample

  • 进入 my-sample 文件夹 初始化 package.json

yarn init --yes
  • 创建 全局链接
 yarn link
  • 在 package.json 中加入 bin 字段,用于指定入口文件
{
  "name": "my-sample",
  "version": "1.0.0",
  "description": "nodejs脚手架",
  "bin": "cli.js",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "MIT"
}
  • 安装 inquirer 和 ejs
yarn add inquirer
yarn add ejs

-根目录下创建 templates,在里面放入 index.html 和 styles.css
index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title><%= name %></title>
</head>
<body>

</body>
</html>

style.css

body {
  margin: 0;
  background-color: #f8f9fb;
}
  • 根目录下创建 cli.js 文件,加入#!/usr/bin/env
#!/usr/bin/env node
//node Node CLI 应用入口文件必须要有这样的文件头

加入执行逻辑代码

#!/usr/bin/env node

// Node CLI 应用入口文件必须要有这样的文件头
// 如果是 Linux 或者 macOS 系统下还需要修改此文件的读写权限为 755
// 具体就是通过 chmod 755 cli.js 实现修改

// 脚手架的工作过程:
// 1. 通过命令行交互询问用户问题
// 2. 根据用户回答的结果生成文件

const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')
const ejs = require('ejs')

inquirer.prompt([
  {
    type: 'input',
    name: 'name',
    message: 'Project name?'
  }
])
.then(anwsers => {
  // console.log(anwsers)
  // 根据用户回答的结果生成文件

  // 模板目录
  const tmplDir = path.join(__dirname, 'templates')
  // 目标目录
  const destDir = process.cwd()

  // 将模板下的文件全部转换到目标目录
  fs.readdir(tmplDir, (err, files) => {
    if (err) throw err
    files.forEach(file => {
      // 通过模板引擎渲染文件
      ejs.renderFile(path.join(tmplDir, file), anwsers, (err, result) => {
        if (err) throw err

        // 将结果写入目标文件路径
        fs.writeFileSync(path.join(destDir, file), result)
      })
    })
  })
})
使用说明:

项目中的模板有两个文件,只是作为例子,可根据自己的需求添加
注: index.html 中的 <title><%= name %></title> 表示 title 为用户定义

  • 1.以上方模板为例,在项目中创建一个文件夹
  • 2.在终端中打开该文件夹
  • 3.运行 my-sample 4.提示? Project name? , 输入名称(该名称为 index.html 中的<title><%= name %></title>),点击回车
  • 4.打开所创建的文件夹,里面出现了模板文件夹下的两个文件,内容 title 都一样, 表示创建成功

grunt构建工具使用

  • 创建项目
  • 在文件中运行 yarn init 初始化package.json
  • 安装grunt依赖, yarn add grunt --dev
  • 在根目录下创建gruntfile.js
gruntfile.js说明
  • Grunt 的入口文件
  • 用于定义一些需要Grunt 自动执行的任务
  • 需要导出一个函数
  • 此函数接收一个 grunt的形参,内部提供一些创建任务时可以用的API
//gruntfile.js
module.exports = (grunt) => {
  grunt.registerTask('foo', () => {
    console.log('hello, grunt')
  })
}

运行:

yarn grunt foo

更多使用:

//gruntfile.js
module.exports = (grunt) => {
  grunt.registerTask('foo', () => {
    console.log('hello, grunt')
  })

  //第二个参数为描述
  grunt.registerTask('bar', '任务描述', () => {
    console.log('other task')
  })

  //默认任务, 直接运行 yarn grunt
  grunt.registerTask('default', () => {
    console.log('default task')
  })

  //两个任务一起执行
  grunt.registerTask('default', ['foo', 'bar'])

  //异步任务,不支持
  // grunt.registerTask('async-task', () => {
  //   setTimeout(() => {
  //     console.log('async task working')
  //   }, 1000)
  // })

  //异步任务正确写法
  grunt.registerTask('async-task', function () {
    let done = this.async()
    setTimeout(() => {
      console.log('async task working')
      done()
    }, 1000)
  })

 //标记错误
  grunt.registerTask('bad', () => {
    console.log('error bar')
    return false //return 后其他任务会执行
  })

  //异步错误
  grunt.registerTask('async-error', function () {
    let done = this.async()
    setTimeout(() => {
      console.log('async task error')
      done(false)
    }, 1000)
  })
}
  • initConfig 用于为任务添加一些配置选项
module.exports = grunt => {
  // grunt.initConfig() 用于为任务添加一些配置选项
  grunt.initConfig({
    // 键一般对应任务的名称
    // 值可以是任意类型的数据
    foo: {
      bar: 'baz'
    }
  })

  grunt.registerTask('foo', () => {
    // 任务中可以使用 grunt.config() 获取配置
    console.log(grunt.config('foo')) //{bar:'baz'}
    // 如果属性值是对象的话,config 中可以使用点的方式定位对象中属性的值
    console.log(grunt.config('foo.bar')) //baz
  })
}
  • grunt 多目标模式
module.exports = grunt => {
  // 多目标模式,可以让任务根据配置形成多个子任务

  // grunt.initConfig({
  //   build: {
  //     foo: 100,
  //     bar: '456'
  //   }
  // })

  // grunt.registerMultiTask('build', function () {
  //   console.log(`task: build, target: ${this.target}, data: ${this.data}`)
  // })

  grunt.initConfig({
    build: {
      options: {
        msg: 'task options'
      },
      foo: {
        options: {
          msg: 'foo target options'
        }
      },
      bar: '456'
    }
  })

  grunt.registerMultiTask('build', function () {
    console.log(this.options())
  })
}
  • loadNpmTasks的使用
    注 : 需要先下载 grunt-contrib-clean 和相关依赖
module.exports = grunt => {
  grunt.initConfig({
    clean: {
      temp: 'temp/**'
    }
  })
  
  grunt.loadNpmTasks('grunt-contrib-clean') //清除 temp 下面的所有文件
}
  • load-grunt-tasks
    注 : 需要先下载 load-grunt-tasks 和相关依赖
const sass = require('sass')
const loadGruntTasks = require('load-grunt-tasks')

module.exports = grunt => {
  grunt.initConfig({
    sass: {
      options: {
        sourceMap: true,
        implementation: sass
      },
      main: {
        files: {
          'dist/css/main.css': 'src/scss/main.scss'
        }
      }
    },
    babel: {
      options: {
        sourceMap: true,
        presets: ['@babel/preset-env']
      },
      main: {
        files: {
          'dist/js/app.js': 'src/js/app.js'
        }
      }
    },
    watch: {
      js: {
        files: ['src/js/*.js'],
        tasks: ['babel']
      },
      css: {
        files: ['src/scss/*.scss'],
        tasks: ['sass']
      }
    }
  })

  // grunt.loadNpmTasks('grunt-sass')
  loadGruntTasks(grunt) // 自动加载所有的 grunt 插件中的任务

  grunt.registerTask('default', ['sass', 'babel', 'watch'])
}

Gulp

基本使用
// // 导出的函数都会作为 gulp 任务
// exports.foo = () => {
//   console.log('foo task working~')
// }

// gulp 的任务函数都是异步的
// 可以通过调用回调函数标识任务完成
const { series, parallel } = require('gulp')
exports.foo = (done) => {
  console.log('foo task working~')
  done() // 标识任务执行完成
}

// default 是默认任务
// 在运行是可以省略任务名参数
exports.default = (done) => {
  console.log('default task working~')
  done()
}

// v4.0 之前需要通过 gulp.task() 方法注册任务
const gulp = require('gulp')

gulp.task('bar', (done) => {
  console.log('bar task working~')
  done()
})

const task1 = (done) => {
  setTimeout(() => {
    console.log('task1 working~')
    done()
  }, 1000)
}

const task2 = (done) => {
  setTimeout(() => {
    console.log('task2 working~')
    done()
  }, 1000)
}

const task3 = (done) => {
  setTimeout(() => {
    console.log('task3 working~')
    done()
  }, 1000)
}

// 让多个任务按照顺序依次执行
exports.foo1 = series(task1, task2, task3)

// 让多个任务同时执行
exports.bar = parallel(task1, task2, task3)

异步任务:
const fs = require('fs')

exports.callback = done => {
  console.log('callback task')
  done()
}

exports.callback_error = done => {
  console.log('callback task')
  done(new Error('task failed'))
}

exports.promise = () => {
  console.log('promise task')
  return Promise.resolve()
}

exports.promise_error = () => {
  console.log('promise task')
  return Promise.reject(new Error('task failed'))
}

const timeout = time => {
  return new Promise(resolve => {
    setTimeout(resolve, time)
  })
}

exports.async = async () => {
  await timeout(1000)
  console.log('async task')
}

exports.stream = () => {
  const read = fs.createReadStream('yarn.lock')
  const write = fs.createWriteStream('a.txt')
  read.pipe(write)
  return read
}

// exports.stream = done => {
//   const read = fs.createReadStream('yarn.lock')
//   const write = fs.createWriteStream('a.txt')
//   read.pipe(write)
//   read.on('end', () => {
//     done()
//   })
// }

gulp 构建的核心工作原理
const fs = require('fs')
const { Transform } = require('stream')

exports.default = () => {
  // 文件读取流
  const readStream = fs.createReadStream('normalize.css')

  // 文件写入流
  const writeStream = fs.createWriteStream('normalize.min.css')

  // 文件转换流
  const transformStream = new Transform({
    // 核心转换过程
    transform: (chunk, encoding, callback) => {
      const input = chunk.toString()
      const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '')
      callback(null, output)
    }
  })

  return readStream
    .pipe(transformStream) // 转换
    .pipe(writeStream) // 写入
}

gulp文件操作API
const { src, dest } = require('gulp')
const cleanCSS = require('gulp-clean-css')
const rename = require('gulp-rename')

exports.default = () => {
  return src('src/*.css')
    .pipe(cleanCSS())
    .pipe(rename({ extname: '.min.css' }))
    .pipe(dest('dist'))
}

更多使用
const { src, dest, parallel, series, watch } = require('gulp')

const del = require('del')
const browserSync = require('browser-sync') 

const loadPlugins = require('gulp-load-plugins')

const plugins = loadPlugins()
const bs = browserSync.create()

const data = {
  menus: [
    {
      name: 'Home',
      icon: 'aperture',
      link: 'index.html'
    },
    {
      name: 'Features',
      link: 'features.html'
    },
    {
      name: 'About',
      link: 'about.html'
    },
    {
      name: 'Contact',
      link: '#',
      children: [
        {
          name: 'Twitter',
          link: 'https://twitter.com/w_zce'
        },
        {
          name: 'About',
          link: 'https://weibo.com/zceme'
        },
        {
          name: 'divider'
        },
        {
          name: 'About',
          link: 'https://github.com/zce'
        }
      ]
    }
  ],
  pkg: require('./package.json'),
  date: new Date()
}

//文件删除
const clean = () => {
  return del(['dist', 'temp'])
}

//样式编译
const style = () => {
  return src('src/assets/styles/*.scss', { base: 'src' })
    .pipe(plugins.sass({ outputStyle: 'expanded' }))
    .pipe(dest('temp'))
    .pipe(bs.reload({ stream: true }))
}

//脚本编译
const script = () => {
  return src('src/assets/scripts/*.js', { base: 'src' })
    .pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
    .pipe(dest('temp'))
    .pipe(bs.reload({ stream: true }))
}

//转换html
const page = () => {
  return src('src/*.html', { base: 'src' })
    .pipe(plugins.swig({ data, defaults: { cache: false } })) // 防止模板缓存导致页面不能及时更新
    .pipe(dest('temp'))
    .pipe(bs.reload({ stream: true }))
}

//转换图片
const image = () => {
  return src('src/assets/images/**', { base: 'src' })
    .pipe(plugins.imagemin()) //压缩图片
    .pipe(dest('dist'))
}

//转换字体
const font = () => {
  return src('src/assets/fonts/**', { base: 'src' })
    .pipe(plugins.imagemin())  //处理图片, 非图片直接拷贝
    .pipe(dest('dist'))
}

//转换配置文件
const extra = () => {
  return src('public/**', { base: 'public' })
    .pipe(dest('dist'))
}

//开发服务器
const serve = () => {
  //监视文件,重新执行某些任务
  watch('src/assets/styles/*.scss', style)
  watch('src/assets/scripts/*.js', script)
  watch('src/*.html', page)
  // watch('src/assets/images/**', image)
  // watch('src/assets/fonts/**', font)
  // watch('public/**', extra)
  watch([
    'src/assets/images/**',
    'src/assets/fonts/**',
    'public/**'
  ], bs.reload)

  // 启动服务器运行文件
  bs.init({
    notify: false,
    port: 2080,
    // open: false,
    // files: 'dist/**',
    server: {
      baseDir: ['temp', 'src', 'public'],
      routes: {
        '/node_modules': 'node_modules'
      }
    }
  })
}

// useref 文件引用处理
const useref = () => {
  return src('temp/*.html', { base: 'temp' })
    .pipe(plugins.useref({ searchPath: ['temp', '.'] }))
    // html js css
    .pipe(plugins.if(/\.js$/, plugins.uglify()))
    .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
    .pipe(plugins.if(/\.html$/, plugins.htmlmin({
      collapseWhitespace: true, //折叠空白字符
      minifyCSS: true, 
      minifyJS: true
    })))
    .pipe(dest('dist'))
}

// 开发阶段执行
const compile = parallel(style, script, page)

// 上线之前执行的任务
const build =  series(
  clean,
  parallel(
    series(compile, useref),
    image,
    font,
    extra
  )
)

const develop = series(compile, serve)

module.exports = {
  clean,
  build,
  develop
}

将本地项目提交到git空仓库
  1. git init
  2. git remote add origin 仓库地址
  3. git status 查看状态
  4. git add .
  5. git commit -m '说明'
  6. git push -u origin master
vite 创建项目

npm init vite@latest
npm init vite-app projectName

git commit 验证eslint

npx mrm@2 lint-staged

相关文章

网友评论

      本文标题:JavaScript工程化(学习笔记)

      本文链接:https://www.haomeiwen.com/subject/iyefwltx.html