前端工程化

2021-01-14  本文已影响0人  lowpoint

为何工程化

在我们的实际开发中,我们想用最新的es语法,想用less,sass等样式预处理。我们想要使用模块化的方式提高项目的可维护性,但是运行环境却不支持。多人协作,代码风格不统一。发布上线需要手动压缩与上传,这些问题的出现需要我们去有一种方式去解决这些问题。工程化就是解决这些问题的一个体现。

工程化的体现

一切以提高效率,降低成本,质量保证为目的的手段都属于工程化。
一些成熟的工程化集成 如vue-cli , angular-cli , create-react-app
前端工程化的实现 nodejs有巨大贡献。

脚手架工具yeoman的使用

1.全局安装yo

cnpm i yo -g //或者 yarn global add yo

2.安装对应的generator

npm i generator-node -g //或者 yarn global add generator-node

3.通过yo运行generator

yo node
yeoman的sub generator使用

1.明确需求。
2.找到合适的Generator。
3.全局范围安装找到的Generator。
4.通过yo安装找到的Generator。
5.通过命令交互填写选项。
6.生成所需要的项目结构。

自定义Generator

基于yeoman搭建自己的脚手架
1.创建Generator模块(generator本质上就是一个NPM模块)


图片.png

yeoman的generator模块名称必须是 generator-<name>

mkdir generator-sample //创建文件
cd generator-sample 
yarn init //初始化
yarn add yeoman-generator //安装基类 提供了一些方法

按照格式创建


图片.png
//作为generator核心入口
//需要导出一个继承自 yeoman generator的类型
//yeoman generator  在工作时会自动调用我们在此类型中定义的一些声明周期方法
//在这些方法中可以通过调用父类提供的一些工具方法实现一些功能  比如文件写入

const Generator = require('yeoman-generator')

module.exports = class extends Generator {
  writing() {
    //yoman 自动在生成文件阶段调用此方法
    //我们在这里尝试往项目目录中写入文件
    // this.fs.write(
    //   this.destinationPath('test.txt'),
    //   Math.random().toString()
    // )

    //通过模板方式写入文件到目标目录
    //三个参数 1.模板文件路径 2.输出文件路径 3.模板数据上下文
    const tmpl = this.templatePath('foo.txt')//模板文件路径
    const output = this.destinationPath('foo.txt')//模板文件路径
    const context = { title: 'hello tem', success: true }//模板数据上下文
    this.fs.copyTpl(tmpl, output, context)
  }
}

通过yarn link链接到全局范围 使其成为一个全局模块包

yarn link

在其它地方就可以运行这个生成器

yo sample //刚才起的生成器名称

创建一个自己的vue Generator

mkdir generator-my-vue
cd generator-my-vue
yarn init
yarn add yeoman-generator
code . //vscode 打开文件

1.还是先创建模板文件


图片.png

2.写模板代码

const Generator = require('yeoman-generator')

module.exports = class extends Generator{
  prompting(){
    return this.prompt([
      {
        type:'input',
        name:'name',
        message:'your project name',
        default:this.appname
      }
    ])
    .then(answers => {
      this.answers = answers
    })
  }
  writing(){
    
  }
}

3.将项目结构放入template文件夹中


图片.png

4.将项目结构可能发生变化的地方用模板语法替换 如:


图片.png
  1. yarn link 到全局
    6.使用 yo my-vue 安装到对应目录

发布generator

//先创建一个本地的git仓库
//先创建一个gitignore
echo node_modules > .gitignore
//初始化一个本地空仓库
git init
//查看本地仓库状态
git status

git add .
//创建一次提交
git commit -m "initial commit"

//提交到远端仓库
//创建一个远端新仓库 gitee或者github
git remote add origin https://gitee.com/wkpkko/generator-myvue.git
//这样为本地仓库添加了一个远端仓库的别名 push的时候可以使用这个别名
git push -u origin master //推送到远端仓库
//通过npm publish 发布这个模块  yarn publish 
yarn publish
//在使用淘宝镜像发布时会出现问题
//发布的时候设置镜像
yarn publish --registry=https://registry.yarnpkg.com
//自动推送到yarn的官方镜像  yarn的镜像与npm镜像时同步的  此时模块发布成功了

//在npm官网  npmjs.com/package/generator-myvue 
//此时模块已经被推送上来了

Plop一个小而美的脚手架工具

主要用于创建项目中特定类型文件的一个小工具,类似与yeoman中的subgeneraor,一般不会单独使用。一般会把plop集成在项目中,用来自动化创建同类型的项目文件

yarn add plop --dev

在项目根目录下创建plopfile.js

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

module.exports = plop => {
  plop.setGenerator('component', {
    description: 'create a component',
    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'
      }
    ]
  })
}

创建模板文件
plop-templates/component.hbs

import React from 'react';

export default () => (
  <div className="{{name}}">
    <h1>{{name}} Component</h1>
  </div>
)

component.css.hbs

.{{name}} {
  
}

component.test.hbs

import React from 'react';
import ReactDOM from 'react-dom';
import {{name}} from './{{name}}';

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<{{name}} />, div);
  ReactDOM.unmountComponentAtNode(div);
});

yarn plop component (刚才定义的生成器的名称)

脚手架工作原理

大部分脚手架都是设置一些问题,通过这些问题为你创建一些模板的项目结构。
脚手架工具就是一个nodecli应用。
通过nodejs开发一个小型的脚手架工具

mkdir sample-cli
cd sample-cli
yarn init//初始化一个package.json文件

//package.json中添加bin字段作为cli的入口文件
{
  "name": "sample-cli",
  "version": "1.0.0",
  "description": "sample cli",
  "bin":"cli.js",
  "main": "index.js",
  "license": "MIT"
}

创建cli.js
cli文件必须要有一个特定的文件头

#!/usr/bin/env node

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

通过yarn link 链接到全局
此时就可以使用sample-cli命令执行


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

node通过用户询问 我们使用inquirer模块

yarn add inquirer
此时我们可以在cli.js载入它

const inquirer = require('inquirer')
inquirer.prompt([
  {
    type: 'input',
    name: 'name',
    message: 'Project name?'
  }
]).then(answer => {
  console.log(answer)
})
图片.png

创建模板文件


图片.png
#!/usr/bin/env node

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

//脚手架的工作过程
//1.通过命令行交互询问用户问题
//2.根据用户回答的结果生成文件
const path = require('path')
const fs = require('fs')
const inquirer = require('inquirer')

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

  //模板目录
  const templDir = path.join(__dirname,'templates')
  //输出目录 命令行执行路径
  const destDir = process.cwd()
  //将模板下的文件全部转换到目录 fs的readdir方法会自动扫描目录下所有文件
  fs.readdir(templDir,(err,files) => { //files拿到所有文件列表
    if(err) throw err
    files.forEach(file => {
      // console.log(file) //每个file就是相对于templates下的相对路径
      //可以通过模板引擎去渲染这个路径所对应的文件
      
    })
  })

})

先安装对应的模板引擎 (安装一个ejs模板引擎)

yarn add ejs
之后引入模板引擎

#!/usr/bin/env node

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

//脚手架的工作过程
//1.通过命令行交互询问用户问题
//2.根据用户回答的结果生成文件
const path = require('path')
const fs = require('fs')
const inquirer = require('inquirer')
const ejs = require('ejs')

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

  //模板目录
  const templDir = path.join(__dirname, 'templates')
  //输出目录 命令行执行路径
  const destDir = process.cwd()
  //将模板下的文件全部转换到目录 fs的readdir方法会自动扫描目录下所有文件
  fs.readdir(templDir, (err, files) => { //files拿到所有文件列表
    if (err) throw err
    files.forEach(file => {
      // console.log(file) //每个file就是相对于templates下的相对路径
      //可以通过模板引擎去渲染这个路径所对应的文件
      //renderFile三个参数 1.文件的绝对路径 2.工作时的数据上下文answer 3.回调函数
      ejs.renderFile(path.join(templDir, file), answer, (err, result) => {
        if (err) throw err
        //通过文件写入写入到目标目录
        fs.writeFileSync(path.join(destDir, file), result)
      })
    })
  })

})

资料来源:拉勾教育-前端训练营

上一篇下一篇

猜你喜欢

热点阅读