前端从零搭建自己的脚手架

蒙面大婶l 2024-06-16 11:33:09 阅读 75

目录

一、什么是脚手架

二、项目的创建

1、包管理工具

2、初始化

3、构建项目的基本结构

4、引入react

5、引入typescript

6、webpack配置

7、文件别名

8、引入less、sass、stylus

三、webpack构建速度优化

1、webpack进度条

2、构建耗时

3、开启持久化存储缓存:

4、开启多线程loader

四、webpack构建产物优化

1、bundle体积分析工具

2、样式提取

3、tree-shaking清理未引用的js

4、tree-shaking清理未引用的css

5、资源懒加载

6、资源预加载

7、gzip压缩

总结:


一、什么是脚手架

        Webpack脚手架是一个工具,用于快速搭建基于Webpack的项目结构。它通常包括了一些预设的配置和插件,以便用户可以快速开始一个新的项目,而不必从头开始配置Webpack。这些脚手架可以帮助开发人员自动处理诸如打包、代码转译、资源管理等繁琐的任务,从而让他们更专注于项目的逻辑实现。

二、项目的创建

1、包管理工具

npm(Node Package Manager)是 Node.js 的默认包管理器,可以用于安装、更新和删除 JavaScript 包。

yarn 是另一个流行的包管理器,由 Facebook 开发。它旨在解决 npm 的一些限制和一些性能安全问题,并且通常比 npm 更快速、稳定。

pnpm 是另一个包管理器,它通过重用文件系统硬链接以及共享依赖来显著减少磁盘空间占用。相比于传统的 npm 和 yarn,pnpm 在安装包时只保存一个拷贝,因此在大型项目中能够节省大量的磁盘空间。

此次项目中我们使用pnpm。

相关资料:pnpm基本使用

2、初始化

新建一个文件夹,然后在根目录打开终端并输入以下命令:

pnpm - v # 初始化 package . json 文件 pnpm init

初始化后会在根目录生成一个package.json 文件:

package.json文件在Webpack中扮演了重要的角色。它用于描述项目的依赖项信息和定义的依赖项,并且可以利用package.json中的脚本命令来执行构建任务。

3、构建项目的基本结构

├── build| ├── webpack.base.ts # 公共配置| ├── webpack.dev.ts # 开发环境配置| └── webpack.prod.ts # 打包环境配置├── public│ └── index.html # html模板├── src| ├── App.tsx| ├── App.css│ └── index.tsx # react应用入口页面└── package.json

index.html先添加一个节点:

<body> <!-- 容器节点 --> <div id="root"></div></body>

4、引入react

我们先简单了解一下什么是react:

        React 是一个用于构建用户界面的 JS 库。它通过组件化的方式让开发者可以轻松地构建交互式的用户界面。React 使用虚拟 DOM 技术,这使得页面在更新时能够更加高效地进行渲染。通过使用 JSX 语法,React 允许开发者将 HTML 结构和 JavaScript 代码结合起来。

安装react依赖:

pnpm i react react - dom # 声明依赖 pnpm i @ types / react @ types / react - dom - D

在src/index.tsx中编写入口文件

import React from 'react';import { createRoot } from 'react-dom/client';import App from './App';const root = document.querySelector('#root')if(root) { createRoot(root).render(<App />)}

在App.css写一个简单的样式:

h2 { color:red;}

以及App.tsx

import React from 'react'import './App.css'function App() { return <h2>你好,蒙面大婶</h2>}export default App

5、引入typescript

简单了解一下什么是typescript(ts):

        TypeScript是一种由微软开发的开源编程语言。TypeScript通过添加静态类型、接口、类和其他高级结构来扩展JavaScript,从而使得在大型项目中更易于维护和阅读。它最终会被编译成纯粹的JavaScript,可以在任何支持JavaScript的地方运行。TypeScript的主要优势之一是提供了更强大的工具和功能,以帮助程序员在开发过程中尽早发现和修复错误。

在终端输入以下命令安装ts:

pnpm i typescript - D pnpm i babel - loader ts - node @ babel / core @ babel / preset - react @ babel / preset-typescript @ babel / preset - env core - js - D
初始化 tsconfig.json :
npx tsc -- init

6、webpack配置

简单了解一下什么是webpack:

        webpack 是一个流行的开源前端模块打包工具,它通过将各种静态资源(如JavaScript、CSS、HTML、图片等)视为模块以及其它的一 些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并基于这些模块生成优化后的静态文件(通常是一个或多bundle)。这些bundle可以被浏览器直接使用,从而提高网页的加载速度和运行效率。

在终端输入以下命令安装依赖:

pnpm i webpack webpack - cli - D pnpm i @ types / node - D

因为需要引入css文件,所以还需要安装相关的loader:

pnpm i style - loader css - loader html - webpack - plugin - D

loader:在Webpack中,loader是用于转换非JavaScript文件(如CSS、图片、字体等)为模块的工具。

配置webpack.base.ts:

import { Configuration } from "webpack";import HtmlWebpackPlugin from "html-webpack-plugin";const path = require("path");const baseConfig: Configuration = { entry: path.join(__dirname, "../src/index.tsx"), // 入口文件 // 打包出口文件 output: { filename: "static/js/[name].js", // 每个输出js的名称 path: path.join(__dirname, "../dist"), // 打包结果输出路径 clean: true, // webpack4需要配置clean-webpack-plugin来删除dist文件,webpack5内置了 publicPath: "/", // 打包后文件的公共前缀路径 }, // loader 配置 module: { rules: [ { test: /.(ts|tsx)$/, // 匹配.ts, tsx文件 use: "babel-loader" }, { test: /.css$/, //匹配 css 文件 use: ["style-loader", "css-loader"], }, ], }, resolve: { extensions: [".tsx", ".ts", ".jsx", ".js"], }, // plugins plugins: [ new HtmlWebpackPlugin({ // 复制 'index.html' 文件,并自动引入打包输出的所有资源(js/css) template: path.join(__dirname, "../public/index.html"), // 压缩html资源 minify: { collapseWhitespace: true, //去空格 removeComments: true, // 去注释 }, }), ],};export default baseConfig; 在根目录新建 babel.config.js ,并输入以下内容:

module.exports = { // 执行顺序由右往左,所以先处理ts,再处理jsx,最后再试一下babel转换为低版本语法 presets: [ [ "@babel/preset-env", { targets: { browsers: ["> 1%", "last 2 versions", "not ie <= 8"] }, useBuiltIns: "usage", // 根据配置的浏览器兼容,以及代码中使用到的api进行引入polyfill按需添加 corejs: 3, // 配置使用core-js使用的版本 loose: true, }, ], // 如果您使用的是 Babel 和 React 17,您可能需要将 "runtime": "automatic" 添加到配置中。 // 否则可能会出现错误:Uncaught ReferenceError: React is not defined ["@babel/preset-react", { runtime: "automatic" }], "@babel/preset-typescript", ],}; 我们需要通过 webpack-dev-server 来启动项目,所以需要安装相关的依赖:

pnpm i webpack-dev-server webpack-merge -D

并配置webpack.dev.ts :

import path from "path";import { merge } from "webpack-merge";import { Configuration as WebpackConfiguration } from "webpack";import { Configuration as WebpackDevServerConfiguration } from "webpack-dev-server";import baseConfig from "./webpack.base";interface Configuration extends WebpackConfiguration { devServer?: WebpackDevServerConfiguration;}const host = "127.0.0.1";const port = "8082";// 合并公共配置,并添加开发环境配置const devConfig: Configuration = merge(baseConfig, { mode: "development", // 开发模式,打包更加快速,省了代码优化步骤 devtool: "eval-cheap-module-source-map", devServer: { host, port, open: true, // 是否自动打开 compress: false, // gzip压缩,开发环境不开启,提升热更新速度 hot: true, // 开启热更新 historyApiFallback: true, // 解决history路由404问题 setupExitSignals: true, // 允许在 SIGINT 和 SIGTERM 信号时关闭开发服务器和退出 进程。 static: { directory: path.join(__dirname, "../public"), // 托管静态资源public文件夹 }, headers: { "Access-Control-Allow-Origin": "*" }, // HTTP响应头设置,允许任何 来源进行跨域请求 },});export default devConfig;

在tsconfig.json中加入一行"jsx":"react-jsx",这样就不需要在tsx文件中手动引入react:

{ "compilerOptions": { "jsx": "react-jsx" }, "include": ["./src"]} 在  package.json 中添加启动脚本:

"scripts": { "dev": "webpack serve -c build/webpack.dev.ts"},

最后在终端运行pnpm dev启动项目,出现以下结果说明运行成功。

配置webpack.prod.ts:

import { Configuration } from "webpack";import { merge } from "webpack-merge";import baseConfig from "./webpack.base";const prodConfig: Configuration = merge(baseConfig, { mode: "production", // 生产模式,会开启tree-shaking和压缩代码,以及其他优化});export default prodConfig; 在 package.json 中添加脚本命令来执行构建任务 :

"scripts": { "build": "webpack -c build/webpack.prod.ts"},

然后运行pnpm build命令,就是使用名为"webpack.prod.ts"的配置文件来进行生产环境下的构建。Webpack会根据该配置文件对项目进行打包和优化,生成用于生产环境部署的文件。

运行后会在根目录生成一个dist文件夹,就是打包、压缩或编译后的文件,如(HTML、CSS、JavaScript文件),用于部署到生产环境。

copy静态资源:

一般 public 文件夹会放一些静态资源(如图片、 css 、 js 文件),可以直接根据绝对路径引入,需要 安装依赖:

pnpm i copy - webpack - plugin - D

修改webpack.base.ts :

const baseConfig: Configuration = { // ... plugins: [ new HtmlWebpackPlugin({ title: "webpack5-react-ts", filename: "index.html", // 复制 'index.html' 文件,并自动引入打包输出的所有资源(js/css) template: path.join(__dirname, "../public/index.html"), inject: true, // 自动注入静态资源 hash: true, cache: false, // 压缩html资源 minify: { removeAttributeQuotes: true, collapseWhitespace: true, //去空格 removeComments: true, // 去注释 minifyJS: true, // 在脚本元素和事件属性中缩小JavaScript(使用UglifyJS) minifyCSS: true, // 缩小CSS样式元素和样式属性 }, }), ],}; 打包配置文件 webpack.prod.ts 中新增 copy 插件配置:

import { Configuration } from "webpack";import { merge } from "webpack-merge";import baseConfig from "./webpack.base";import path from "path";import CopyPlugin from "copy-webpack-plugin";const prodConfig: Configuration = merge(baseConfig, { mode: "production", // 生产模式,会开启tree-shaking和压缩代码,以及其他优化 plugins: [ new CopyPlugin({ patterns: [ { from: path.resolve(__dirname, "../public"), // 复制public下文件 to: path.resolve(__dirname, "../dist"), // 复制到dist目录中 filter: (source) => !source.includes("index.html"), // 忽略index.html }, ], }), ],});export default prodConfig; 在 public 中新增一个名为  favicon.ico 图标文件,在 index.html 中引入:

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <!-- 绝对路径引入图标文件 --> <link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>webpack5-react-ts</title></head><body> <!-- 容器节点 --> <div id="root"></div></body></html> 在终端执行 pnpm build命令 ,就可以看到 public 下的 favicon.ico 图标文件被复制到 dist 文件中 ,再运行pnpm dev命令就好了。

7、文件别名

配置  webpack.base.ts :

resolve: { extensions: [".ts", ".tsx", ".js", ".jsx", ".less", ".css"], // 别名需要配置两个地方,这里和 tsconfig.json alias: { "@": path.join(__dirname, "../src") },},

配置tsconfig.json:

{ "compilerOptions": { // ... "baseUrl": ".", "paths": { "@/*": ["src/*"] }, },}

8、引入less、sass、stylus

        在Webpack中,Less、Sass和Stylus是CSS预处理器,它们允许开发人员使用类似编程语言的语法来编写样式表,并将其转换为浏览器可识别的标准CSS。

在终端输入以下命令安装依赖:

pnpm i less less - loader sass - loader sass stylus stylus - loader - D
在 webpack.base.ts 添加相关的 loader :

const cssRegex = /\.css$/;const sassRegex = /\.(scss|sass)$/;const lessRegex = /\.less$/;const stylRegex = /\.styl$/;const styleLoadersArray = [ "style-loader", { loader: "css-loader", options: { modules: { localIdentName: "[path][name]__[local]--[hash:5]", }, }, },];const baseConfig: Configuration = { // ... module: { rules: [ // ... { test: lessRegex, use: [ ...styleLoadersArray, { loader: 'less-loader', options: { lessOptions: { // 如果要在less中写js的语法,需要加这一配置 javascriptEnabled: true } } } ] }, { test: sassRegex, use: [ ...styleLoadersArray, { loader: 'sass-loader', options: { implementation: require('sass') // 使用dart-sass代替node-sass } } ] }, { test: stylRegex, use: [ ...styleLoadersArray, 'stylus-loader' ] } ] }}

三、webpack构建速度优化

1、webpack进度条

        webpack进度条以图形方式显示项目运行情况,使我们可以清晰地了解项目的运行程度。

首先安装依赖:

pnpm i webpackbar -D
webpack.base.ts 中引入:

// ...import WebpackBar from 'webpackbar';// ...const baseConfig: Configuration = { // ... // plugins 的配置 plugins: [ // ... new WebpackBar({ color: "#85d", // 默认green,进度条颜色支持HEX basic: false, // 默认true,启用一个简单的日志报告器 profile:false, // 默认false,启用探查器。 }) ],};export default baseConfig;

在运行后,终端的输出为:

2、构建耗时

可以清楚的看到每个步骤的花费时长,安装依赖:

pnpm i speed-measure-webpack-plugin -D
在build中新建webpack.analy.ts:

import { Configuration } from "webpack";import prodConfig from "./webpack.prod"; // 引入打包配置import { merge } from "webpack-merge"; // 引入合并webpack配置方法const SpeedMeasurePlugin = require("speed-measure-webpack-plugin"); // 引入webpack打包速度分析插件;const smp = new SpeedMeasurePlugin(); // 实例化分析插件// 使用smp.wrap方法,把生产环境配置传进去,由于后面可能会加分析配置,所以先留出合并空位const analyConfig: Configuration = smp.wrap(merge(prodConfig, {}));export default analyConfig; 修改 package.json 添加启动 webpack 打包分析脚本命令,在 scripts 新增:

"scripts": { "build:analy": "cross-env NODE_ENV=production BASE_ENV=production webpack -c build/webpack.analy.ts"}

在终端执行 pnpm build:analy。

3、开启持久化存储缓存:

        通过对 文件做哈希对比来验证文件前后是否一致,如果一致则采用上一次的缓存,可以极大地节省时间。 修改webpack.base.ts:

module.exports = { // ... cache: { type: 'filesystem', // 使用文件缓存 },}

4、开启多线程loader

        多线程loader是一种可以并行加载多个资源或数据的系统或程序组件。它利用多线程技术,允许同时从不同来源加载数据,以提高整体加载速度和性能。

安装依赖:

pnpm i thread-loader -D
修改 webpack.base.ts :

module: { rules: [ { test: /\.(ts|tsx)$/, // 匹配ts和tsx文件 use: [ { loader: 'thread-loader', options: { wokers: 4 // 进程数 } }, 'babel-loader'] }, ]}

四、webpack构建产物优化

1、bundle体积分析工具

        bundle体积分析工具是一个用于可视化显示 bundle 结构和大小的工具。它可以帮我们查看 JavaScript 和其他资源文件在 bundle 中的大小、依赖关系等信息。

安装依赖:

pnpm i webpack-bundle-analyzer -D

修改 webpack.analy.ts :

import { Configuration } from "webpack";import { merge } from "webpack-merge";import prodConfig from "./webpack.prod";const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");// 引入webpack打包速度分析插件const smp = new SpeedMeasurePlugin();// 使用smp.wrap方法,把生产环境配置传进去,由于后面可能会加分析配置,所以先留出合并空位const analyConfig: Configuration = smp.wrap(merge(prodConfig, {plugins: [new BundleAnalyzerPlugin() // 配置分析打包结果插件]}))export default analyConfig;

2、样式提取

安装依赖:

pnpm i mini-css-extract-plugin -D
修改 webpack.base.ts:

const MiniCssExtractPlugin = require('mini-css-extract-plugin')const isDev = process.env.NODE_ENV === 'development' // 是否是开发模式const styleLoadersArray = [ isDev ? "style-loader" : MiniCssExtractPlugin.loader, // 开发环境使用stylelooader,打包模式抽离css { loader: "css-loader", options: { modules: { localIdentName: "[path][name]__[local]--[hash:5]", }, }, }, 'postcss-loader'];

修改 webpack.prod.ts:

const MiniCssExtractPlugin = require('mini-css-extract-plugin')const prodConfig: Configuration = merge(baseConfig, { // ... plugins: [ // ... new MiniCssExtractPlugin({ filename: 'static/css/[name].css' // 抽离css的输出目录和名称 }), ], });export default prodConfig;

完成以上步骤后,Webpack会把样式从JavaScript bundle中提取出来,并生成单独的CSS文件。

3、tree-shaking清理未引用的js

        tree-shaking 的字面意思是树摇,伴随着摇树这个动作,树上的枯枝败叶都会被摇下来。是指通过静态代码分析来识别和删除 JavaScript 中未被使用的未引用代码(通常是未被导入或调用的代码)。这种优化技术有助于减小最终打包后的文件大小,因为它能够消除那些未被使用的模块、变量、函数或方法。

在 src/components 目录下新增 Demo1.tsx 、 Demo2.tsx 两个组件:

Demo1.tsx:

import React from "react";function Demo1() { return <h3>我是Demo1组件</h3>}export default Demo1

Demo2.tsx:

import React from "react";function Demo2() { return <h3>我是Demo2组件</h3>}export default Demo2 在 src/components 目录下新增 index.ts :

export { default as Demo1 } from './Demo1'export { default as Demo2 } from './Demo2' 在 App.tsx 中引入两个组件,但只使用 Demo1 组件:

import { Demo1, Demo2 } from '@/components' function App() { return <> <Demo1 /> </>}export default App

执行程序,在 main.js 中只搜索到了 Demo1 ,Demo2 就被 tree-shaking 删除掉了。

4、tree-shaking清理未引用的css

和清除JS类似,这个树摇会清除掉不需要的css样式,安装依赖:

pnpm i purgecss-webpack-plugin glob-all -D
修改 webpack.prod.ts :

import MiniCssExtractPlugin from 'mini-css-extract-plugin'const globAll = require('glob-all')const { PurgeCSSPlugin } = require('purgecss-webpack-plugin')const prodConfig: Configuration = merge(baseConfig, { // ... plugins: [ // 抽离css插件 new MiniCssExtractPlugin({ filename: 'static/css/[name].[contenthash:8].css' }), new PurgeCSSPlugin({ paths: globAll.sync( [`${path.join(__dirname, '../src')}/**/*`, path.join(__dirname, '../public/index.html')], { nodir: true } ), // 用 only 来指定 purgecss-webpack-plugin 的入口 only: ["dist"], safelist: { standard: [/^ant-/] // 过滤以ant-开头的类名,哪怕没用到也不删除 } }), ]})

5、资源懒加载

        资源懒加载指的是将某些模块或资源延迟加载到应用程序。资源懒加载的作用主要在于提高应用程序的性能、减少加载时间,并允许按需加载模块和其他资源,从而提升用户体验。

新建  src/components/LazyDemo.tsx :

import React from "react";function LazyDemo() { return <h3>我是懒加载组件组件</h3>}export default LazyDemo 修改 App.tsx :

import React, { lazy, Suspense, useState } from 'react'const LazyDemo = lazy(() => import('@/components/LazyDemo')) // 使用import语法配合react的Lazy动态引入资源function App() { const [show, setShow] = useState(false) // 点击事件中动态引入css, 设置show为true const handleOnClick = () => { import('@/App.css') setShow(true) } return ( <> <h2 onClick={handleOnClick}>展示</h2> {/* show为true时加载LazyDemo组件 */} {show && <Suspense fallback={null}><LazyDemo /></Suspense>} </> )}export default App 点击展示时,才会加载 app.css 和 LazyDemo 的资源。

6、资源预加载

        预加载可以确保关键资源在用户需要时已经存在,从而加快页面响应速度,对于一些异步加载的内容,可以在页面加载完成之前预先获取,以便在需要时立即使用

在 src/components 目录下新建 PreloadDemo.tsx 、 PreFetchDemo.tsx :

// src/components/PreFetchDemo.tsximport React from "react";function PreFetchDemo() { return <h3>我是PreFetchDemo组件</h3>}export default PreFetchDemo 在 import 引入动态资源时使用 webpack 的魔法注释, 修改 App.tsx :

import React, { lazy, Suspense, useState } from 'react'// prefetchconst PreFetchDemo = lazy(() => import( /* webpackChunkName: "PreFetchDemo" */ /*webpackPrefetch: true*/ '@/components/PreFetchDemo'))function App() { const [show, setShow] = useState(false) const onClick = () => { setShow(true) } return ( <> <h2 onClick={onClick}>展示</h2> {/* show为true时加载组件 */} {show && ( <> <Suspense fallback={null}><PreloadDemo /></Suspense> <Suspense fallback={null}><PreFetchDemo /></Suspense> </> )} </> )}export default App

运行后点击展示

7、gzip压缩

        webpack构建过程中可以自动对文件进行gzip压缩,从而减小文件大小,提高页面加载速度。

安装依赖:

pnpm i compression-webpack-plugin -D
修改 webpack.prod.ts :

const glob = require('glob')const CompressionPlugin = require('compression-webpack-plugin')module.exports = { // ... plugins: [ // ... new CompressionPlugin({ test: /.(js|css)$/, // 只生成css,js压缩文件 filename: '[path][base].gz', // 文件命名 algorithm: 'gzip', // 压缩格式,默认是gzip threshold: 10240, // 只有大小大于该值的资源会被处理。默认值是 10k minRatio: 0.8 // 压缩率,默认值是 0.8 }) ]}

运行程序后,在JS的目录下回生成一个.gz结尾的文件。

总结:

        最后,一个基本的脚手架就搭建完成了。当然本文也有许多不完善的地方,后续我会不断完善优化该项目,希望可以通过本文让您对webpack工具有一个更加深入和全面的认识,以便于应对以后项目中配置更改。如果您发现本文中有错误的地方,欢迎评论以便及时更正!!!



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。