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


theme: channing-cyan
highlight: monokai-sublime

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

小书介绍

前言

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

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

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

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

Babel

到这里得开始关注自身代码的需求了,大多数项目最基础也需要使用到ES6语法了。虽然目前有好一部分浏览器都支持,但是难免会有某些不支持的情况,最好的做法全部转成ES5再给浏览器解析。

我们选择目前非常热门的ES编译器Babel来进行转换,需要安装@babel/core@babel/preset-env两个插件和创建babel.config.js配置文件。

npm i -D @babel/core @babel/preset-env 
// babel.config.js
module.exports = {
  presets: [
    [
      "@babel/preset-env", // ES
      {
        targets: {
          browsers: ["> 0.25%", "not dead"],
        },
      },
    ],
  ],
};

Babel & Webpack

配置好babel后还需要借助Webpack来对js文件进行构建转换,需要安装babel-loader来配置。

npm i -D babel-loader
// webpack.base.js
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"),
    }),
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        },
      },
    ],
  },
};

这样一来babel-loader就会自动使用babel.config.js的配置了,我们添加ES6语法验证一下。

// 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);

console.log([1, 2, 3].map((n) => n + 1)); // 使用ES6箭头函数

接着执行npm run build看看构建结果

babel01.png

可以看到成功将ES6的箭头函数转换成ES5的匿名函数

Babel & ESLint

前面可以知道我们能够使用哪种版本的ES6语法取决于babel的配置,但有没想过那eslint检查的是哪个版本的ES6语法?

如果版本不一致,可能会导致babel支持某个ES6语法而eslint不支持时反而出现报错的情况。

例如下面使用ES2021的语法ESLint就会提示解析错误:

// 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);

console.log([1, 2, 3].map((n) => n + 1));

let d = null;
d ||= "daotin";
console.log(d);

babel02.png

下面我们再看看构建会不会有问题:

npm run build

babel03.png

可以看出来webpack通过babel的配置一样能够转换ES2021的语法,主要原因是在于@babel/preset-env默认就是支持最新的ES语法。

babel04.png

而ESLint会报错的原因是在于我们配置的是ES6版本的语法,也就是2015的版本。

babel05.png

这个时候可以通过配置es2021来支持这个版本的语法:

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

但是这样还是没办法完全避免版本不一致的问题,最好的方法还是两者使用同一种配置,所以我们需要用到插件@babel/eslint-parser来解决这个问题。

npm i -D @babel/eslint-parser
// .eslintrc.js
module.exports = {
  env: {
    browser: true,  // 支持浏览器环境
    node: true,     // 识别 CommonJS
    es2021: true,   // 识别 ES 的代码
  },
  // 继承ESLint的规则集
  extends: [
    "eslint:recommended",           // ESLint自带
    "plugin:prettier/recommended"   // Prettier
  ],
  overrides: [
    {
      files: ["**/*.{js,jsx}"],          // 只处理 js 和 jsx 文件
      parser: "@babel/eslint-parser",    // 使用 babel 来解析 js 文件
      parserOptions: {
        sourceType: "module",            // 支持 import/export
        allowImportExportEverywhere: false,
        ecmaFeatures: {
          globalReturn: false,
        },
        babelOptions: {
          configFile: './babel.config.js', // 指定babel配置文件
        },
      },
    }
  ]
};

这样一来就可以用一个babel.config.js文件来配置Webpack的构建和ESLint的代码检查了。

TypeScript

通过前面的babel配置,终于可以肆意使用最新版的ES语法了。但是如今大部分企业实际上都在使用TypeScript了,而且对代码的可维护性、壮健性都有很大的提升。
所以我们这里也需要对TypeScript也做下支持,首先通过命令创建tsconfig.json配置文件。

# 创建tsconfig.json
npx tsc --init
// 修改tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "useDefineForClassFields": true,
    "module": "esnext",
    "moduleResolution": "node",
    "baseUrl": "./",
    "sourceMap": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "alwaysStrict": true,
    "skipLibCheck": true
  }
}

创建个TS文件试试:

// src/index.ts
interface Test {
  a: number;
  b: string;
}

const test: Test = {
  a: 111,
  b: "111",
};

console.log(test);

typescript01.png

结果ESLint这边解析出了问题,那后面我们加上ESLint的检测吧。

TypeScript & ESLint

首先得安装相应的三个插件typescript@typescript-eslint/parser@typescript-eslint/eslint-plugin

npm install -D 
  typescript # TypeScript
  @typescript-eslint/parser # TypeScript 解析器
  @typescript-eslint/eslint-plugin # TypeScript 规则集和插件功能

再来修改ESLint配置文件

// .eslintrc.js
module.exports = {
  ...
  overrides: [
    ...
    {
      files: ["**/*.{ts,tsx}"],              // 只处理 ts 和 tsx 文件
      parser: "@typescript-eslint/parser",   // 解析 TypeScript
      parserOptions: {
        project: ["./tsconfig.json"],        // 指定ts配置文件
      },
      extends: [
        "plugin:@typescript-eslint/recommended",                          // 官方语法检查
        "plugin:@typescript-eslint/recommended-requiring-type-checking",  // 类型检查
      ],
      plugins: ["@typescript-eslint"],
    }
  ]
};

这下你会发现之前src/index.ts的ESLint报错提示消失了。

TypeScript & Webpack

现在光能用TypeScript写代码还不行,还得让Webpack支持TS转JS才行。
之前我们ES6转ES5时是让Webpack通过Babel配置来转换,这次TypeScript也可以通过Babel配置给Webpack转换。

npm i -D @babel/preset-typescript
// babel.config.js
module.exports = {
  presets: [
    [
      "@babel/preset-env", // ES
      {
        targets: {
          browsers: ["> 0.25%", "not dead"],
        },
      },
    ],
    [
      "@babel/preset-typescript", // TS
    ],
  ],
};

对应的Webpack配置入口babel-loader解析后缀都需要修改。

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

module.exports = {
  entry: {
    index: path.resolve(__dirname, "../src/index.ts"),
  },
  output: {
    path: path.resolve(__dirname, "../dist"),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../public/index.html"),
      favicon: path.resolve(__dirname, "../public/logo.svg"),
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(js|ts)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        },
      },
    ],
  },
};

到这里再去执行npm run build就会发现构建没有问题了。

前面使用@babel/preset-typescript进行转换的方式的优点在于构建效率快,但缺点是缺少类型检查
为了兼容不同的使用人群,这边也介绍下使用ts-loader的方式,优点是具有类型检查,缺点则是构建效率较慢

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

module.exports = {
  entry: {
    index: path.resolve(__dirname, "../src/index.ts"),
  },
  output: {
    path: path.resolve(__dirname, "../dist"),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../public/index.html"),
      favicon: path.resolve(__dirname, "../public/logo.svg"),
    }),
  ],
  module: {
    rules: [
      {
        // test: /\.(js|ts)$/,
        test: /\.(js)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        },
      },
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        exclude: /node_modules/,
        options: {
          configFile: path.resolve(process.cwd(), 'tsconfig.json')
        },
      },
    ],
  },
};
npm run dev # 构建看看效果

这个时候我们再尝试写下错误的类型,就会看到终端会出现报错信息。

// src/index.ts
interface Test {
  a: number;
  b: string;
  c: string;
}

const test: Test = {
  a: 111,
  b: "111",
};

console.log(test);

typescript02.png

最后总结下,这里我们介绍了两种方式来转换TS:

  • @babel/preset-typescript 这种方式的好处在于构建效率快,但缺点是缺少类型检查
  • ts-loader 这种方式的优点是构建时会进行类型检查,但构建效率慢

具体使用哪种方式来转换TS,就看个人的取舍了,这里不做推荐。

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容