从0到1搭建企业级vue3脚手架 (一)

262 阅读8分钟

哈喽大家好,我是haibin。这是我写的第一册技术小书,在掘金会分几篇发布,也可以直接点击下面在线访问全册,希望大家喜欢!

小书介绍

前言

以往像我们搭建vue项目,第一时间想到的就是vue-cli直接命令生成脚手架。

这种方式看似非常轻松、方便,平时写写业务代码或许无所谓,但直到需要你优化项目、解决原理层棘手问题的时候你才猛然发现,过于依赖这个工具反而会导致我们对于底层原理一概不知。这个时候就会措手不及,才逐渐开始去了解底层的部分。

与其到时被动,不如现在主动从0到1去搭建vue3脚手架!

这本小书会手把手地教你如何从“空文件夹”搭建到企业级脚手架。

起步

空文件夹

首先,我们来创建一个空文件夹。

mkdir vue3-scaffolding-tutorial-example
cd vue3-scaffolding-tutorial-example

说明文档

接着创建README.md文件,用于项目说明。

# vue3-scaffolding-tutorial-example

git忽略配置

接着是git忽略配置文件.gitignore,Github官方提供了不同环境下的.gitignore模板
我们这里直接使用提供的Node.gitignore即可。

index.html

模仿Vue Cli的目录结构创建public和src目录

mkdir public
mkdir src

顺便也把最基础的index.html也创建了吧,放到public/index.html

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

package.json

npm的由来

在GitHub还没有兴起的年代,前端是通过网址来共享代码。比如说你想使用jQuery,那么你得点击jQuery网站上提供的链接来下载jQuery,再放到自己的网站上使用。

到GItHub兴起之后,社区中也有人使用GitHub的下载功能。但当一个网站依赖的代码越来越多,发现这是一件非常麻烦的事情:

  • 去 jQuery官网 下载jQuery
  • 去 BootStrap官网 下载BootStrap
  • 去 Underscore官网 下载Underscore
  • ...

IsaacZSchlueter.png

有些程序员就受不鸟了,一个拥有三大美德的程序员 Isaac Z. Schlueter (上图)给出一个解决方案:用一个工具把这些代码集中到一起来进行管理。

RyanDahl.png

后面跟Node.js的作者Ryan Dahl(上图)合作,成为Node.js内置的包管理器。

所以这个工具也就叫做npm,全称是Node Package Manager(运行在Node.js上的JavaScript程序),而我们待会要创建的package.json也就是这个工具的包描述文件。

它的大致思路是:

  1. 让不同插件的作者通过npm publish把代码提交到registry上
  2. 社区里的其他人就想用这些插件,就把插件名写到包描述文件package.json
  3. 运行npm install,npm就会帮他们下载代码到本地的node_modules目录里

创建package.json

由于Node.js内置了npm,所以我们可以直接使用命令npm init -y来进行创建。

会生成大致如下包含包自身描述的内容

{
  "name": "vue3-scaffolding-tutorial-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/haibin-007/vue3-scaffolding-tutorial-example.git"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/haibin-007/vue3-scaffolding-tutorial-example/issues"
  },
  "homepage": "https://github.com/haibin-007/vue3-scaffolding-tutorial-example#readme"
}

构建工具的选择

既然是企业级脚手架,那么肯定得选择比较成熟且稳定的工具。
目前市场上比较合适的选项有:webpack、rollup、gulp、vite build_tools.png

...

无论从市场或功能层面,webpack都比较适合我们的场景。

Webpack

先把webpack包及其命令行工具安装了。
为了后续便于维护,我们专门建一个webpack目录。

npm i -D webpack webpack-cli 

mkdir webpack

接着创建webpack/webpack.base.js文件以及我们指向的入口文件src/index.js

// webpack/webpack.base.js
const path = require('path');

module.exports = {
  entry: {
    index: path.resolve(__dirname, '../src/index.js'),
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
  }
}

// src/index.js
console.log('hello world!');

再创建webpack/webpack.dev.jswebpack/webpack.prod.js,针对于开发和生产环境的配置文件。

这里我们需要使用webpack-merge插件来合并配置项,webpack-dev-server用于提供开发环境服务。

npm i -D webpack-merge webpack-dev-server
// webpack/webpack.dev.js
const webpackMerge = require('webpack-merge');
const baseConfig = require('./webpack.base');
const path = require('path');

module.exports = webpackMerge.merge(baseConfig, {
  mode: 'development',
  devServer: {
    host: '0.0.0.0',
    port: 3000
  },
});

// webpack/webpack.prod.js
const webpackMerge = require('webpack-merge');
const baseConfig = require('./webpack.base');

module.exports = webpackMerge.merge(baseConfig, {
  mode: 'production'
});

再将开发和构建的命令加入到package.json。

"scripts": {
  "dev": "webpack serve --config webpack/webpack.dev.js",
  "build": "webpack --config webpack/webpack.prod.js"
},

尝试执行下命令npm run dev

webpack01.png

结果我们看到它只是拿到我们本地的public/index.html静态文件,并没有绑定src/index.js
所以这里我们需要用到一个webpack插件html-webpack-plugin来生成已绑定的index.html

npm i -D html-webpack-plugin
// webpack.base.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: {
    index: path.resolve(__dirname, '../src/index.js'),
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../public/index.html'), 
      favicon: path.resolve(__dirname, '../public/logo.svg'), // 顺便加入favicon
    }),
  ]
}

由于它是生成在output指定的dist目录,所以我们还需配置webpack-dev-server的静态目录为dist。

// webpack.dev.js
devServer: {
  host: '0.0.0.0',
  port: 3000,
  static: {
    directory: path.join(__dirname, '../dist')
  }
},

接着再执行npm run dev我们则会看到已经绑定上了。

webpack02.png

顺带一提,这个ws实际上就是webpack-dev-server的热更新的通讯原理,通过websocket侦测代码的变化,从而通知本地server进行更新。

再执行npm run build试试。

npm run build
npm install -g http-server 
http-server dist # 这里我们借助http-server访问dist来验证结果

webpack03.png

结果如我们所愿跟dev的呈现结果一致,只是进一步对index.html做了压缩。

ESLint

到这里我们算是把最基础的构建部分给弄好了。
既然要构建企业级的脚手架,代码的规范性肯定也少不了,这里我们选取当下前端最热门的代码规范检查工具ESLint

接着我们安装eslint以及创建它的配置文件.eslintrc.js,顺便也把.eslintignore忽略文件也创建了

npm i -D eslint
// .eslintrc.js
module.exports = {
  env: {
    browser: true,  // 支持浏览器环境
    node: true,     // 识别 CommonJS
    es6: true,      // 识别 ES 的代码
  },
  // 继承ESLint的规则集
  extends: [
    "eslint:recommended",           // ESLint自带
  ]
};

// .eslintignore  需要时再添加配置项

我们要知道ESLint规则默认配置是没有限制的也就是无规则的,需要什么规范我们就自己手动去配置里加规则,这里使用了eslint自身提供的规则集eslint:recommended

随意在src/index.js里加代码试试效果。

console.log('hello world!');
const test = 123;

eslint01.png

这里可以看到提示信息违反了规则no-unused-vars,也就是不允许声明未使用的变量,再手动加上去使用就可以避免违反规则了。

console.log('hello world!');
const test = 123;
console.log(test);

但我们想象一下,如果每次有违反规则的时候我们都得手动去修复是不是特别麻烦?况且我们一般都是统一用业界写好的规则集,没有多少人会特地去熟悉这么多的规则。

那这个痛点我们该怎么来解决呢?

Prettier

这个痛点能够使用Prettier来解决,Prettier是通过结合eslint的规则来进行修复。

接着我们来安装prettier,以及创建它的配置文件.prettierrc.js

npm i -D prettier
// .prettierrc.js
module.exports = {}

PrettierESLint完全不一样,它的默认配置并非是零,而是每个选项都有默认值,所以我们直接不进行配置即可。

加入一段代码来验证下效果

console.log("hello world!");
const test = 123;
console.log(test);

let   a   = 1;
const     b   =       2
const   c   = 333;
console.log('result'
  ,a,
  b,
  c)

执行如下命令即可生效

npx prettier --write .\src\index.js
console.log("hello world!");
const test = 123;
console.log(test);

let a = 1;
const b = 2;
const c = 333;
console.log("result", a, b, c);

通过命令来修复特别麻烦,所以我们通过结合编辑器插件来自动修复。

prettier01.png

vscode插件商店搜索prettier即可找到对应插件,安装完后我们需要创建vscode的项目配置文件.vscode/settings.json来覆盖默认配置。

// .vscode/settings.json
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode"
}

配置了保存时格式化使用prettier格式化两项,现在再保存文件就会自动使用prettier格式化了。

这时你会发现.eslintrc.js文件保存时也被格式化了,之前的注释也不那么整齐了。

// .eslintrc.js
module.exports = {
  env: {
    browser: true, // 支持浏览器环境
    node: true, // 识别 CommonJS
    es6: true, // 识别 ES 的代码
  },
  // 继承ESLint的规则集
  extends: [
    "eslint:recommended", // ESLint自带
  ],
};

我们可以创建一个.prettierignore忽略文件来跳过指定文件或目录:

// .prettierignore
.eslintrc.js

这样一来就不会被格式化了。

最后我们顺便将编辑器风格配置文件.editorconfig也集成到prettier中。

// .editorconfig
root = true

[*]
end_of_line = lf
charset = utf-8
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
tab_width = 4

[*.md]
trim_trailing_whitespace = false

[Makefile]
indent_style = tab
// .prettierrc.js
module.exports = {
  useEditorConfig: true,
};

下图在vscode的Prettier插件输出中也可以看出配置生效了。

prettier02.png

ESLint & Prettier

前面已经配置好了Prettier,接着就是将ESLint的规则使用到Prettier的格式化中。

需要安装两个插件:

npm i -D eslint-plugin-prettier eslint-config-prettier
  • eslint-plugin-prettier作用是添加prettier的代码风格规则,使用prettier来自动修复代码
  • eslint-config-prettier作用是用来禁用掉eslint部分与prettier相冲突的规则的
// .eslintrc.js
module.exports = {
  env: {
    browser: true,  // 支持浏览器环境
    node: true,     // 识别 CommonJS
    es6: true,      // 识别 ES 的代码
  },
  // 继承ESLint的规则集
  extends: [
    "eslint:recommended",           // ESLint自带
    "plugin:prettier/recommended"   // Prettier
  ]
};

这时error的修复选项就会有prettier了。

prettier_eslint01.png

Webpack & ESLint

为了能够在构建过程中就能发现ESlint的错误,这里我们可以使用插件eslint-webpack-pluginESLintWebpack进行绑定。

npm i -D eslint-webpack-plugin

再在webpack/webpack.base.js进行配置即可。

// webpack.base.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ESLintWebpackPlugin = require('eslint-webpack-plugin');

module.exports = {
  entry: {
    index: path.resolve(__dirname, '../src/index.js'),
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../public/index.html'), 
      favicon: path.resolve(__dirname, '../public/logo.svg'), // 顺便加入favicon
    }),
    new ESLintWebpackPlugin({
      extensions: ['js','ts','vue']
    }),
  ]
}