前端工程化的一些实践

821 阅读6分钟

写在前面的话

小时候总是以为,等高考完上了大学了就不用学习了。后来,上了大学,总以为考上研究生就不用学习了。等上了研究生,便以为工作了就不用学习了。刚工作那会,一直告诉自己,头两年是积累的过程,等过了这两年就轻松了。时间慢慢地就走到了现在,积累了工作经验,可我却意识到,我还是要继续学习。学习没有终点,我们应该终身学习。也许我的觉悟相比于很多人有点晚,这种觉悟好像让人生多了些悲情的色彩,可也让生活变得更加笃定,方向也更加的清晰。

清明的时候看电影,看到了一句话,直击心灵---我们经常在正确的事情和容易的事情之间做选择。对啊,有那么多个时候,我们总是选择容易的事情去做,明明我们知道正确的路是哪个方向。很庆幸看到了这句话,以后当面对选择的时候,多想想正确的那个。

老规矩:上猫,愿我的Leo可以一直是个健康快乐的小猫咪,比心心❤️

微信图片_20210426121552.jpg


一、利用Plop生成项目中相同的组件

1、安装plop

yarn add plop --dev

2、在项目根目录下新建plopfile.js文件,作为plop的入口文件

//plopfile.js
//plop的入口文件,需要导出一个函数
//此函数接口一个plop对象,用于创建生成器任务

module.exports = (plop) => {
//********component 为生成器的名称********
  plop.setGenerator('component', {
    description: 'create a component',
    //提示用户手动输入组件名称,默认为‘MyComponent’
    prompts: [
      {
        type: 'input',
        name: 'name',
        message: 'component name',
        default: 'MyComponent',
      },
    ],
    actions: [
      {
        type: 'add', //添加一个全新的文件
        path: 'src/components/{{name}}/{{name}}.js',
        templateFile: 'plop-templates/component.hbs',
      },
      {
        type: 'add', //添加一个全新的文件
        path: 'src/components/{{name}}/{{name}}.css',
        templateFile: 'plop-templates/component.css.hbs',
      },
      {
        type: 'add', //添加一个全新的文件
        path: 'src/components/{{name}}/{{name}}.test.js',
        templateFile: 'plop-templates/component.test.hbs',
      },
    ],
  })
}

3、在plop-templates文件中创建模板文件

//以component.hbs为例,{{name}}为手动输入的组件的名称
import React from 'react';
export default ()=>(
<div className="{{name}}">
    <h1>{{name}} Component</h1>
</div>
)

4、使用yarn plop命令创建文件

yarn plop component //component为上文提到的生成器的名称

结合项目经理,有一些自己的想法

在构建一个大型VUE项目的时候,会有很多时候会创建结构类似的组件。这个时候就可以使用golp工具。 比如,可以利用VUE的分模块管理,在创建每个模块的时候,都会生成相应的路由、api、store。就可以事先生成glop模板之后,一句命令搞定。美滋滋····


二、 封装自己的Gulp CLI

1、安装

yarn add gulp --dev //安装依赖
code gulpfile.js //根目录下添加gulpfile.js文件作为gulp的入口文件

2、使用

  • gulpfile.js文件中,可以使用exports将任务导出
  • 在命令行中执行yarn gulp 任务名称即可执行该任务。当定义的任务名称为default的时,执行任务可以省略任务名称。
//gulpfile.js
exports.foo = (done) => {
  console.log('foo--------')
  done() //标识任务完成
}
exports.default = (done) => {
  console.log('default--------')
  done() //标识任务完成
}          //yarn gulp即可执行该任务

image.png

3、gulp组合任务

  • series可以定义串行任务。将任务依次执行。
  • parallel可以执行串行任务,同时启动任务。任务之间互不干扰。
const { series, parallel } = require('gulp')
const task1 = (done) => {
  console.log('1--------')
  done() //标识任务完成
}
const task2 = (done) => {
  console.log('2--------')
  done() //标识任务完成
}
const task3 = (done) => {
  console.log('3--------')
  done() //标识任务完成
}
exports.foo = series(task1, task2, task3) //串行任务
exports.bar = parallel(task1, task2, task3) //并行任务

image.png

image.png

4、gulp构建过程核心工作原理

  • 通过读取流将文件读取出来fs.createReadStream
  • 通过转换流将文件加工为想要的格式new Transform
  • 通过输出流将文件输出到指定位置fs.createWriteStream
const fs = require('fs')
const { Transform } = require('stream')
exports.default = () => {
  //文件读取流
  const read = fs.createReadStream('base.css')
  //文件写入流
  const write = fs.createWriteStream('base.min.css')
  //文件转换流
  const transform = new Transform({
    transform: (chunk, encoding, callback) => {
      //核心转换过程实现
      //chunk => 读取流中读取到的内容(bugffer)
      const input = chunk.toString()
      const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '')
      callback(null, output)
    },
  })
  //把读取出来的文件流导入到写入文件流
  read
    .pipe(transform) //转换
    .pipe(write) //写入
  return read
}

5、Gulp文件操作API

gulp提供了很多API,方便文件的读写

//public文件,不需要被加工直接拷贝的文件
//src目录下的所有文件都会被构建
//sass,es6需要在构建过程中被编译
//图片&字体文件被压缩

const { src, dest, parallel, series, watch } = require('gulp')

const del = require('del')

const browerSync = require('browser-sync')
const bs = browerSync.create()
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
// const sass = require('gulp-sass')
// const babel = require('gulp-babel')
// const swig = require('gulp-swig')
// const imagemin = require('gulp-imagemin')

//data为模板引擎中需要用到的数据

const data = {
  a: [],
  b: '11',
  data: new Date(),
}

const clean = () => {
  return del(['dist', 'temp']) //promise
}

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 }))
}
const page = () => {
  return src('src/*.html', { base: 'src' })
    .pipe(plugins.swig({ data }))
    .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('piblic/**', { 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('piblic/**', extra)
  watch(['src/assets/images/**', 'src/assets/fonts/**', 'piblic/**'], bs.reload)
  //启动web服务,在浏览器中保证所见即所得
  bs.init({
    notify: false,
    server: {
      baseDir: ['temp', 'src', 'public'],
      //   port: 2080,
      //   open: false, //取消自动打开浏览器
      //   files: 'dist/*',
      routes: {
        '/node_modules': 'node_modules',
      },
    },
  })
}

//自动将构建注释中引用到的文件进行合并到一个文件中
const useref = () => {
  return (
    src('temp/*.html', { base: 'dist' })
      .pipe(plugins.useref({ searchPath: ['dist', '.'] }))
      //htm 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('release'))
  )
}

const compile = parallel(style, script, page, image, font)
//上线之前执行的任务
const build = series(
  clean,
  parallel(series(compile, useref), image, font, extra)
)
//开发阶段执行的任务
const develop = series(compile, serve)

module.exports = {
  clean,
  build,
  develop,
}


三、 发布Generator

发布Generator,其实就是发布一个npm模块。

1、将代码同步到远程仓库

步骤:

  • 在github上创建一个仓库
  • 将本地代码推送到远程仓库的master分支
git remote add origin '你的仓库地址'    //关联远程仓库
git push -u origin master    //推送本地代码到远程仓库的master分支

2、申请npm账号并验证

对于第一次发布的小伙伴,首先要申请一个npm账号并登陆后再执行publish操作

npm adduser //根据提示输入用户名和密码
npm login //根据提示输入用户名密码邮箱信息
npm whoami //检测是否申请成功

此时,邮箱会收到npm发的邮件,根据邮件提示进行验证。然后就可以执行publish操作。

3、发布

在执行publish命令的时候,会报错。因为我本地使用的是淘宝镜像源,它是只读镜像,所以往npm仓库去发布代码的时候会报错。 image.png

解决办法:

yarn publish --registry=https://registry.yarnpkg.com //设置yarn镜像源

image.png

然后就可以在npm官网上找到你发布的npm包啦~~