共计 19880 个字符,预计需要花费 50 分钟才能阅读完成。
简介:本篇文章次要解说如何从一个空目录开始,建设起一个基于 webpack + react + typescript 的标准化前端利用。
作者 | 刘皇逊 (恪语) 起源 | 阿里开发者公众号前言本篇文章次要解说如何从一个空目录开始,建设起一个基于 webpack + react + typescript 的标准化前端利用。技术栈: webpack5 + React18 + TS 工程化: eslint + prettier + husky + git hooks 反对图片、less、sass、fonts、数据资源(JSON、csv、tsv 等)、Antd 按需加载以及主题反对热更新、资源压缩、代码拆散(动静导入、懒加载等)、缓存、devServer 背景在我的项目开发中,咱们能够应用 create-react-app 或者飞冰等脚手架工具,那么,为什么咱们要本人来搭建一个标准化我的项目?起因当咱们应用优良的脚手架工具开发我的项目时,当然会晋升很多便当,他们的性能更全面、性能更弱小,然而在这些值得学习的楷模背后,咱们须要从零开始,入手去实现每一个细节和性能,看的再多都不如本人入手实现一个 demo 更有成果。并且入手实际也能够帮忙咱们了解我的项目打包和编译的原理,进而晋升本人的技术熟练度,扩大咱们的知识面。Webpack 实现工程化方方面面的性能,天然不是 all in one code 实现的。从 Webpack 的设计理念和实现原理中,咱们能接触到工程化方面的常识:架构扩大、插件化、缓存机制。学习 Webpack 也代表着学习前端的发展趋势:例如在 webpack 的竟对 Vite 上,咱们能够学到 bundleless 的理念,跳过了传统的打包这个概念,并且其余先进理念都是咱们须要去学习的中央。开发中,咱们发现应用 def、aone 等生成一个成熟的前端我的项目模版,不难会发现,我的项目中的 babel、weback、prettier、loader 等配置文件缺失,而且难以批改现成的脚手架配置,可扩大能力较弱。导致在性能优化方面能做的工作无限,使得开发受到限制。我的项目构造目录├── dist // 默认的 build 输入目录
├── .husky // pre-commit hook
├── webpack.config.js // 全局配置文件及 webpack 配置文件
├── test // 测试目录
└── src // 源码目录
├── assets // 公共的文件(如 image、css、font 等)├── components // 我的项目组件
├── constants // 常量 / 接口地址等
├── routes // 路由
├── utils // 工具库
├── pages // 页面模块
├── Home // Home 模块,倡议组件对立大写结尾
├── ...
├── App.tsx // react 顶层文件
├── typing // ts 类型文件
├── .editorconfig // IDE 格局标准
├── .eslintignore // eslint 疏忽
├── .eslintrc // eslint 配置文件
├── .gitignore // git 疏忽
├── .prettierrc // prettierc 配置文件
├── .babelrc // babel 配置文件
├── LICENSE.md // LICENSE
├── package.json // package
├── README.md // README
├── tsconfig.json // typescript 配置文件依赖 “dependencies”: {
"antd": "^4.22.4", // 懂得都懂
"react": "^18.2.0", // 懂得都懂
"react-dom": "^18.2.0" // 懂得都懂
},
“devDependencies”: {
// babel 全家桶
"@babel/core": "^7.18.10",
"@babel/plugin-proposal-class-properties": "^7.18.6", // React class 反对
"@babel/plugin-transform-runtime": "^7.18.10", // 抽离提取 Babel 的注入代码,避免反复加载,减小体积
"@babel/preset-env": "^7.18.10", // 提供的预设,容许咱们应用最新的 JavaScript
"@babel/preset-react": "^7.18.6", // react 反对
// ts 类型查看
"@types/node": "^18.6.4",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
// @types 结尾的是对应包的 TypeScript 类型申明
"@typescript-eslint/eslint-plugin": "^5.33.0",
"@typescript-eslint/parser": "^5.33.0",
// webpack loader:解析对应文件
"csv-loader": "^3.0.5",
"sass-loader": "^13.0.2",
"xml-loader": "^1.2.1",
"ts-loader": "^9.3.1",
"less-loader": "^11.0.0",
// eslint 全家桶
"eslint": "^8.21.0",
"eslint-config-ali": "^14.0.1", // ali 前端规约
"eslint-config-prettier": "^8.5.0", // 敞开所有不必要或可能与 [Prettier] 抵触的规定
"eslint-import-resolver-typescript": "^3.4.0", // 增加 ts 语法反对 eslint-plugin-import
"eslint-plugin-import": "^2.26.0", // ES6+ import/export 语法反对
"eslint-plugin-prettier": "^4.2.1", // prettier 语法反对
"eslint-plugin-react": "^7.30.1", // react 语法反对
"eslint-plugin-react-hooks": "^4.6.0", // hooks 语法反对
"eslint-webpack-plugin": "^3.2.0",
// webpack plugin
"fork-ts-checker-webpack-plugin": "^7.2.13", // 防止 webpack 中检测 ts 类型
"html-webpack-plugin": "^5.5.0", // 简化 HTML 文件的创立,配合 webpack 蕴含 hash 的 bundle 应用
"mini-css-extract-plugin": "^2.6.1", // css 拆分
"optimize-css-assets-webpack-plugin": "^6.0.1", // css 压缩
"terser-webpack-plugin": "^5.3.3", // 应用 terser 压缩 js(terser 是一个治理和压缩 ES6+ 的工具)"webpack-bundle-analyzer": "^4.5.0", // webpack 打包体积可视化剖析
"webpack-cli": "^4.10.0", // 提供脚手架命令
"webpack": "^5.74.0", // webpack 引擎
"webpack-dev-server": "^4.9.3", // 开发环境的 live server
// 工具
"husky": "^8.0.1", // 主动配置 Git hooks 钩子
"less": "^4.1.3", // css 类型
"sass": "^1.54.3", // css 类型
"typescript": "^4.7.4", // ts
"lint-staged": "^13.0.3", // 对暂存的 git 文件运行 linter
// prettier 格式化
"prettier": "^2.7.1",
"pretty-quick": "^3.1.3", // 在更改的文件上运行 prettier
}实现过程我的项目初始化首先从一个空目录开始,对我的项目初始化:mkdir demo
cd demo
git init
npm initReact 和 Babel 引入对于一个 React 我的项目,咱们首先要装置 React,写一个 Hello World!装置咱们次要的我的项目依赖:tnpm i -S react react-dom 因为咱们的浏览器不反对最新的 ECMAScript 语法,所以咱们须要 Babel 来本义为 ES5 或者 ES6。装置咱们的 Babel 来进步兼容性:tnpm i -D @babel/core babel-preset-env babel-preset-react @babel/plugin-proposal-class-properties@babel/core: babel 转码的外围引擎 babel-preset-env: 增加对 ES5、ES6 的反对 babel-preset-react: 增加对 JSX 的反对 @babel/plugin-proposal-class-properties: 对 React 中 class 的反对 Webpack 引入 tnpm i -D webpack webpack-cli webpack-dev-server html-webpack-pluginwebpack: weback 插件的外围依赖 webpack-cli: 为插件提供命令行工具 webpack-dev-server: 帮忙启动 live serverhtml-webpack-plugin: 帮忙创立 HTML 模版 Babel 配置.babelrc 中增加根本配置:{
“presets”: [“@babel/react”, “@babel/env”],
“plugins”: [“@babel/plugin-proposal-class-properties”]
}Babel PluginBabel 是代码转换器,借助 Babel,咱们能够应用最风行的 js 写法,而 plugin 就是实现 Babel 性能的外围。
这里的配置是为了反对 react 中 class 的写法。Babel PresetBabel 的 Plugin 个别拆成尽可能小的粒度,开发者能够按需引进,例如 ES6 到 ES5 的性能,官网提供了 20+ 插件,这样能够进步性能和扩展性,然而很多时候一一引入就很让人头大,而 Babel Preset 就是为此而生,能够视为 Presets 是相干 Plugins 的汇合。@babel/react: 反对了 React 所有的转码需要 @babel/env: 不夸大滴讲,仅须要它本人外部的配置项,就能够实现古代 JS 工程简直所有的转码需要 Webpack 根本配置新建一个 webpack.config.js 文件。//webpack.config.js
const path = require(‘path’);
const HtmlWebpackPlugin = require(‘html-webpack-plugin’);
module.exports = {
entry: ‘./src/index.js’,
output: {
path: path.join(__dirname, '/dist'),
filename: 'bundle.js'
},
devServer: {
port: 8080
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins:[
new HtmlWebpackPlugin({template: path.join(__dirname,'/src/index.html')
})
]
}entry: 入口,开始打包的终点 output: 打包文件的地址 devServer: live server 配置 test: 应用 loader 的文件类型 loader: 将要应用的 loaderPackage.json 根本配置 ”start”: “webpack serve –mode development –open –hot”,
“build”: “webpack –mode production”mode: process.env.NODE_ENV –> development, 为 modules 和 chunks 启用有意义的名称 open: 通知 server 在服务启动后关上默认浏览器 hot: 开启热更新写一个 React Demo 目前的我的项目构造如下图所示:
js 和 html 文件如下图所示:
最初,只有 start 一下,我的项目就会启动在 8080 端口。TypeScript 配置 tnpm install -D typescript ts-loader @types/node @types/react @types/react-domtypescript: TypeScript 的次要引擎 ts-loader: 本义.ts –> .js 并打包 @types/node @types/react @types/react-dom: 对 node、react、react dom 类型的定义同时在根目录退出 tsconfig.json 来对 ts 编译进行配置://_tsconfig.json_
{
“compilerOptions”: {
"outDir": "./dist/",
"noImplicitAny": true,
"module": "es6",
"target": "es5",
"jsx": "react",
"allowJs": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "Node"
}
}最初在 webpack 中增加对 ts 的反对。增加 ts-loader://_webpack.config.js_
…
{
test: /.tsx?$/,
exclude: /node_modules/,
loader: ‘ts-loader’
}
… 设置 resolve 属性,来指定文件如何被解析://_webpack.config.js_
…
resolve:
{
extensions: [‘.tsx’, ‘.ts’, ‘.js’],
}
…rename 入口://_webpack.config.js_
…
entry: “./src/index.tsx”,
… 最初启动一下 server 来看一下 ts 配置是否正确。
上述咱们的配置其实相当于执行了一次:npx create-react-app my-app –template typescript 在这种流程下很是麻烦,将 *.ts 提供给 TypeScript,而后将运行的后果提供给 Babel,而且还要借助很多 loader。
那么咱们能不能简化一下这样的流程,因为 Babel7 中提供的 babel-loader 就能够完满进行编译 ts,答案是能够的,这种形式间接简化了过程。
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
loader: ['babel-loader']
}
]
}, 并且在.babelrc 中也只多了一行 @babel/preset-typescript,这种配置更简略,而且打包速度更快一点,逻辑更加清晰。那么为什么还要在我的项目中应用 ts-loader 呢?ts-loader 在外部是调用了 TypeScript 的官网编译器 — tsc。所以,ts-loader 和 tsc 是共享 tsconfig.json,所以会提供残缺的报错信息,ts-loader 也与 vscode 提供的语法校验体现统一而 @babel/preset-typescript 有的时候会无奈提供残缺的报错信息和类型提醒治理资源 webpack 只能了解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 可能去解决其余类型的文件,并将它们转换为无效的模块中。loader 中,test 属性能够辨认出哪些文件会被转换;use 属性能够定义出转换时,应该是用哪个 loader。CSS、Less、Sass 装置 loader:tnpm i -D less less-loader style-loader css-loader sass sass-loaderwebpack 配置://_webpack.config.js_
…
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
loader: 'ts-loader',
},
{test: /\.(less|css)$/,
exclude: /\.module\.less$/,
use: [
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: !!DEV,
},
},
{
loader: 'less-loader',
options: {sourceMap: !!DEV,},
},
],
},
{test: /\.(sass|scss)$/,
use: [
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: !!DEV,
},
},
{
loader: 'sass-loader',
options: {sourceMap: !!DEV,},
},
],
},
… 图片、JSON 资源对于图片和字体,咱们能够应用内置的 Assets Modules 来轻松地把这些内容加到咱们的零碎中,对于类型,咱们能够抉择:asset/resource 发送一个独自的文件并导出 URL。asset/inline 导出一个资源的 data URI。asset/source 导出资源的源代码。asset 在导出一个 data URI 和发送一个独自的文件之间主动抉择。//_webpack.config.js_
…
module: {
rules: [{
test: /\.png/,
type: 'asset/resource'
}]
},
… 对于其余类型资源,咱们须要装置 csv-loader、xml-loader 等://_webpack.config.js_
…
{
test: /.(csv|tsv)$/i,
use: [‘csv-loader’],
},
{
test: /.xml$/i,
use: [‘xml-loader’],
},
… 搭建开发环境目前,咱们的利用曾经能够失常运行 tsx 文件,并且在本地进行调试和开发,那么咱们来看看如何设置一个开发环境,来使开发变得更加轻松。//_webpack.config.js_
…
const {DEV, DEBUG} = process.env;
process.env.BABEL_ENV = DEV ? ‘development’ : ‘production’;
process.env.NODE_ENV = DEV ? ‘development’ : ‘production’;
…
mode: DEV ? ‘development’ : ‘production’,
devtool: DEV && ‘source-map’,
… 咱们能够从 process.env 中获取环境变量来辨别开发环境和生产环境。当 webpack 在本地打包代码时,咱们能够应用 inline-source-map,能够将编译后的代码映射回原始源代码,这样在报错的时候,谬误就会被定为到确切的文件和行数。当然,在生产环境中,为了爱护隐衷,最好把这个设置动静关掉。在开发环境中,webpack-dev-server 会为你提供一个根本的 web server,并且具备实时从新加载性能。欠缺打包配置与缓存咱们心愿每次打包都把上次的打包文件删除,能够应用 CleanWebpackPlugin://_webpack.config.js_
…
const {CleanWebpackPlugin} = require(‘clean-webpack-plugin’);
module.exports = {
plugins: [
new CleanWebpackPlugin(),
]
}
… 并且,在咱们生产环境,咱们心愿改变后的新版本能够抛弃缓存,并且没有改变的版本能够保留缓存;然而在开发环境,咱们不心愿有缓存,而是每次都是拿到最新的资源。所以,须要对 webpack config 做一次拆分:分成 webpack.prod.js 生产环境打包配置 webpack.dev.js 开发环境打包配置外面的区别次要在于打包后的文件名称、sourceMap 等。生产环境 contenthash:只有模块的内容扭转,才会扭转 hash 值:output: {
filename: 'js/[name].[contenthash:8].js', // contenthash:只有模块的内容扭转,才会扭转 hash 值
}, 开发环境 output: {
filename: ‘js/[name].[hash:8].js’,
}性能优化打包剖析工具能够应用 webpack-bundle-analyzer 来剖析咱们打包资源的大小:const {BundleAnalyzerPlugin} = require(‘webpack-bundle-analyzer’);
plugins: [
DEBUG && new BundleAnalyzerPlugin(),
]同时设置 package.json 的启动项资源压缩 OptimizeCSSAssetsPlugin 次要用来优化 css 文件的输入,包含摈弃反复的款式定义、砍掉款式规定中多余的参数、移除不须要的浏览器前缀等。TerserPlugin 次要用来优化 js 体积,包含重命名变量,甚至是删除整个的拜访不到的代码块。//_webpack.config.js_
…
const OptimizeCSSAssetsPlugin = require(‘optimize-css-assets-webpack-plugin’);
const TerserPlugin = require(‘terser-webpack-plugin’);
…
optimization: {
minimizer: [
new TerserPlugin({
parallel: false,
terserOptions: {
output: {comments: false,},
},
}),
new OptimizeCSSAssetsPlugin({}),
],
minimize: !DEV,
splitChunks: {
minSize: 500000,
cacheGroups: {vendors: false,},
},
},
… 代码拆散资源拆散 1)多入口 webpack 内置的个性可能把代码拆散到不同的 bundle 中,而后能够按需加载或并行加载这些文件。代码拆散能够用于获取更小的 bundle,以及管制资源加载优先级,如果应用正当,会极大影响加载工夫。//_webpack.config.js_
…
entry: {
index: ‘./src/index.js’,
another: ‘./src/another-module.js’,
},
output: {
filename: ‘[name].bundle.js’
…2)Tree ShakingWebpack5 在生产环境曾经集成了 Tree Shaking 性能,不必的代码会被 shaking 掉:// webpack.config.js
module.exports = {
// …
mode: ‘production’,
}; 然而在开发环境中须要手动配置(Not Recommend):// webpack.config.js
module.exports = {
// …
mode: ‘development’,
optimization: {
usedExports: true,
}
}; 处于好奇,webpack 是如何完满的避开没有应用的代码的呢?很简略:就是 Webpack 没看到你应用的代码。Webpack 跟踪整个应用程序的 import/export 语句,因而,如果它看到导入的货色最终没有被应用,它会认为那是未援用代码 (或叫做“死代码”—— dead-code),并会对其进行 tree-shaking。死代码并不总是那么明确的。上面是一些例子:// test.js
// 这会被看作“活”代码,不会做 tree-shaking
import {add} from ‘./math’
console.log(add(5, 6))// 导入但没有赋值给 JavaScript 对象,也没有在代码里用到
// 这会被当做“死”代码,会被 tree-shaking
import {add, minus} from ‘./math’
console.log(‘hello webpack’)// 导入整个库,然而没有赋值给 JavaScript 对象,也没有在代码里用到
// 十分奇怪,这居然被当做“活”代码,因为 Webpack 对库的导入和本地代码导入的解决形式不同。
import {add, minus} from ‘./math’ // 死的
import ‘lodash’ // 活的
console.log(‘hello webpack’)
所以对于这种三方库咱们能够应用上面的 Shimming 办法。留神 Webpack 不能百分百平安地进行 tree-shaking。有些模块导入,只有被引入,就会对应用程序产生重要影响。一个很好的例子就是全局样式表,或者设置全局配置的 JavaScript 文件。Webpack 认为这样的文件有“副作用”。具备副作用的文件不应该做 tree-shaking,因为这将毁坏整个利用。比拟好的通知 Webpack 你的代码有副作用的办法就是在 package.json 外面设置 sideEffects。{
“name”: “your-project”,
“sideEffects”: [“./src/some-side-effectful-file.js”, “*.css”]
}3)Shimming 预置依赖对于下面的 lodash 库无奈被 shaking,咱们能够应用细粒度 shimming 预置的办法来优化,首先引入 ProvidePlugin 插件,把应用程序中的模块依赖,改为一个全局变量依赖,让咱们先移除 lodash 的 import 语句,改为通过插件提供它,并且提取出 join 办法来全局应用它:// _src/index.tsx
console.log(join([‘hello’, ‘webpack’], ‘ ‘))
// webpack.config.js
plugins: [
new webpack.ProvidePlugin({
//_: 'lodash'
// 如果没正文的话,须要这样援用 console.log(_.join(['hello', 'webpack'], ' '))
join: ['lodash', 'join'],
})
]细粒度 Shimming 一些遗留的模块依赖的 this 指向的 window 对象,咱们能够应用 import-loaders,它对依赖 window 对象下的全局变量(比方 $ 或 this)的第三方模块十分有用。CommonJS 上下文中,这将会变成一个问题,也就是说此时的 this 指向的是 module.exports。在这种状况下,你能够通过应用 imports-loader 笼罩 this 指向:// webpack.config.js
module: {
rules: [
{test: require.resolve('./src/index.js'),
use: 'imports-loader?wrapper=window',
},
]
},4)公共局部提取避免反复能够应用 splitChunk,提取出代码中的公共局部://_webpack.config.js_
…
minimize: !DEV,
splitChunks: {
minSize: 500000,
cacheGroups: {vendors: false,},
},
…minSize:造成一个新代码块最小的体积 cacheGroups:这里开始设置缓存的 chunks5)按需拆散在 React 我的项目中,代码按需拆散能够应用如下办法,webpack 把 import() 作为一个分离点(split-point),并把引入的模块作为一个独自的 chunk。import() 将模块名字作为参数并返回一个 Promoise 对象,即 import(name) -> Promise。//_index.tsx_
…
const WdAndDxEntry = lazy(() => import(/ webpackChunkName: “wd-and-dx” / ‘../../old-code/component/wd-and-dx/entry’));
const WdAndDxFallback = () => ()
const SSRCompatibleSuspense = (props: Parameters< typeof Suspense>[‘0’]) => {
const isMounted = useMounted();
if (isMounted) {
return < Suspense {...props} />;
}
return < >{props.fallback}< />;
}
…
return (
< SSRCompatibleSuspense fallback={< WdAndDxFallback />}>
< WdAndDxEntry
className=""
data={data}
style={{height: 150,}}
/>
< /SSRCompatibleSuspense>
); 6)拆散三方库配置 dependOn option 选项,这样能够在多个 chunk 之间共享模块://_webpack.config.js_
…
module.exports = {
entry: {
index: {
import: './src/index.js',
dependOn: 'shared',
},
another: {
import: './src/another-module.js',
dependOn: 'shared',
},
shared: ‘lodash’,
}
}
…CSS 拆散该插件 MiniCssExtractPlugin 将 CSS 提取到独自的文件中。它为每个蕴含 CSS 的 JS 文件创建一个 CSS 文件。//_webpack.config.js_
…
const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’);
…
{
test: /.(sass|scss)$/,
use: [
{loader: MiniCssExtractPlugin.loader,},
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: !!DEV,
},
},
{
loader: 'sass-loader',
options: {sourceMap: !!DEV,},
},
],
},
…
DEBUG && new BundleAnalyzerPlugin(),
new MiniCssExtractPlugin({
filename: ‘[name].css’,
chunkFilename: ‘[name].css’,
}),
… 进步构建速度当我的项目体积增大时,编译工夫也随之减少。其中工夫大头就是 ts 的类型检测耗时。ts-loader 提供了一个 transpileOnly 选项,它默认为 false,咱们能够把它设置为 true,这样我的项目编译时就不会进行类型查看,也不会输入申明文件。//_webpack.config.js_
…
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {transpileOnly: true}
}
]
}
]
}
… 能够看一下开关这个选项后的前后比照:开启查看前 $ webpack –mode=production –config ./build/webpack.config.js
Hash: 36308e3786425ccd2e9d
Version: webpack 4.41.0
Time: 2482ms
Built at: 12/20/2019 4:52:43 PM
Asset Size Chunks Chunk Names
app.js 932 bytes 0 [emitted] main
index.html 338 bytes [emitted]
Entrypoint main = app.js
[0] ./src/index.ts 14 bytes {0} [built]
Child html-webpack-plugin for “index.html”:
1 asset
Entrypoint undefined = index.html
[0] ./node_modules/html-webpack-plugin/lib/loader.js!./index.html 489 bytes {0} [built]
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
+ 1 hidden module
✨ Done in 4.88s. 敞开查看后 $ webpack –mode=production –config ./build/webpack.config.js
Hash: e5a133a9510259e1f027
Version: webpack 4.41.0
Time: 726ms
Built at: 12/20/2019 4:54:20 PM
Asset Size Chunks Chunk Names
app.js 932 bytes 0 [emitted] main
index.html 338 bytes [emitted]
Entrypoint main = app.js
[0] ./src/index.ts 14 bytes {0} [built]
Child html-webpack-plugin for “index.html”:
1 asset
Entrypoint undefined = index.html
[0] ./node_modules/html-webpack-plugin/lib/loader.js!./index.html 489 bytes {0} [built]
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
-
1 hidden module
✨ Done in 2.40s.From 4.88s –> 2.4s,然而短少了类型查看。这里官网举荐了一个解决方案,应用 fork-ts-checker-webpack-plugin,它在一个独自的过程上运行类型查看器,此插件应用 TypeScript 而不是 webpack 的模块解析,有了 TypeScript 的模块解析,咱们不用期待 webpack 编译。能够极大放慢编译速度。//_webpack.config.js_
…
module: {
rules: [{ test: /\.tsx?$/, use: [ { loader: 'ts-loader', options: {transpileOnly: true} } ] }
]
},
plugins: [
new ForkTsCheckerWebpackPlugin()
]
… 用 editorconfig 对立编辑器标准在根目录新建.editorconfig 即可,留神不要与已有的 lint 规定抵触:// .editorconfighttp://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[makefile]
indent_style = tab
indent_size = 4Antd 配置 babel 中配置按需加载:{
“presets”: [“@babel/react”, “@babel/env”],
“plugins”: [
"@babel/plugin-proposal-class-properties",
[
"import",
{
"libraryName": "antd",
"libraryDirectory": "es",
"style": true // or 'css'
},
"antd"
]
]
}webpack 中定制主题:module: {
rules: [
// 解决 .css
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
// 解决 .less
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
// less-loader
{
loader: 'less-loader',
options: {
lessOptions: {
// 替换 antd 的变量,去掉 @ 符号即可
// https://ant.design/docs/react/customize-theme-cn
modifyVars: {'primary-color': '#1DA57A',},
javascriptEnabled: true, // 反对 js
},
},
},
],
},
]
}
留神款式必须加载 less 格局,一个常见的问题就是引入了多份款式,less 的款式被 css 的款式笼罩了。ESlint 配置 ESlint 次要性能蕴含代码格局和代码品质的校验,并且能够配置 pre-commit 来标准代码的提交。tnpm install -D eslint eslint-webpack-plugin @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-reacteslint: eslint 次要引擎 eslint-webpack-plugin: webpack loader@typescript-eslint/parser: 帮忙 ESlint lint ts 代码 @typescript-eslint/eslint-plugin: 蕴含 TS 扩大规定的插件 eslint-plugin-react: 蕴含 React 扩大规定的插件 ESlint 配置文件 // eslintrc
module.exports = {
parser: ‘@typescript-eslint/parser’, // ESlint Parser
extends: [
'plugin:react/recommended', // 从 @eslint-plugin-react 中抉择举荐的规定
'plugin:@typescript-eslint/recommended', // 从 @typescript-eslint/eslint-plugin 抉择举荐的规定
],
parserOptions: {
ecmaVersion: 2018, // 帮忙转化最先进的 ECMAScript 性能
sourceType: 'module', // 容许 imports 的用法
ecmaFeatures: {jsx: true, // JSX 兼容},
},
rules: {
},
settings: {
react: {version: 'detect', // 通知 eslint-plugin-react 自动检测最新版本的 react},
},
};Prettier 配置尽管 ESLint 也能够校验代码格局,但 Prettier 更善于,所以我的项目中个别会搭配一起应用。为了防止二者的抵触,个别的解决思路是禁掉 ESLint 中与 Prettier 抵触的规定,而后应用 Prettier 做格式化,ESLint 做代码校验。prettier 配置文件 {
“arrowParens”: “avoid”,
“bracketSpacing”: true,
“embeddedLanguageFormatting”: “auto”,
“htmlWhitespaceSensitivity”: “css”,
“insertPragma”: false,
“jsxBracketSameLine”: true,
“jsxSingleQuote”: false,
“printWidth”: 100,
“proseWrap”: “preserve”,
“quoteProps”: “as-needed”,
“requirePragma”: false,
“semi”: true,
“singleQuote”: true,
“tabWidth”: 2,
“trailingComma”: “es5”,
“useTabs”: true,
“vueIndentScriptAndStyle”: false
} 代码提交标准 prettier 只是保障了在通过编辑器(vs code)进行格式化代码的时候,格式化成须要的格局(当然能够通过配置 onSave 在代码保留时主动格式化),然而无奈保障所有人都会被动进行。因而进行主动格式化显得十分重要,而主动格式化的是机会抉择 pre-commit 最失当,通过 git hook,可能在 commit 之前格式化好代码(如果曾经 commit,会将暂存转为提交,生成提交记录,须要回滚才会撤销)。tnpm i -D pretty-quick prettier husky pretty-quick: 配合 git-hooks 进行代码检测,并且 fixhusky: 能够通过配置的形式来应用 git-hooks,防止手动批改 package.json 设置 ”pretty”: “./node_modules/.bin/pretty-quick –staged”
…
“husky”: {
"hooks": {"pre-commit": "tnpm run pretty"}
},Webpack 残缺配置最初贴一下残缺的配置,因为 Aone 公布自动更新版本号,所以不必拆分 config 文件来依据环境设置缓存,并且配置曾经尽可能简化,拆分反而会减少保护老本。//_webpack.config.js_
//webpack.config.js
const path = require(‘path’);
const HtmlWebpackPlugin = require(‘html-webpack-plugin’);
const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’);
const OptimizeCSSAssetsPlugin = require(‘optimize-css-assets-webpack-plugin’);
const TerserPlugin = require(‘terser-webpack-plugin’);
const ForkTsCheckerWebpackPlugin = require(‘fork-ts-checker-webpack-plugin’);
const {BundleAnalyzerPlugin} = require(‘webpack-bundle-analyzer’);
const ESLintPlugin = require(‘eslint-webpack-plugin’);
const {DEV, DEBUG} = process.env;
process.env.BABEL_ENV = DEV ? ‘development’ : ‘production’;
process.env.NODE_ENV = DEV ? ‘development’ : ‘production’;
module.exports = {
entry: ‘./src/index.tsx’,
output: {
path: path.join(__dirname, '/dist'),
filename: 'bundle.js',
clean: true,
},
devServer: {
port: 8080,
},
mode: DEV ? ‘development’ : ‘production’,
devtool: DEV && ‘source-map’,
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
loader: 'ts-loader',
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
// 解决 .less
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
// less-loader
{
loader: 'less-loader',
options: {
lessOptions: {
// 替换 antd 的变量,去掉 @ 符号即可
// https://ant.design/docs/react/customize-theme-cn
modifyVars: {
'primary-color': '#1DA57A',
'border-color-base': '#d9d9d9', // 边框色
'text-color': '#d9d9d9'
},
javascriptEnabled: true, // 反对 js
},
},
},
],
},
{test: /\.(sass|scss)$/,
use: [
{loader: MiniCssExtractPlugin.loader,},
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: !!DEV,
},
},
{
loader: 'sass-loader',
options: {sourceMap: !!DEV,},
},
],
},
{
test: /\.png/,
type: 'asset/resource',
},
{test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
{test: /\.(csv|tsv)$/i,
use: ['csv-loader'],
},
{
test: /\.xml$/i,
use: ['xml-loader'],
},
],
},
optimization: {
minimizer: [
new TerserPlugin({
parallel: false,
terserOptions: {
output: {comments: false,},
},
}),
new OptimizeCSSAssetsPlugin({}),
],
minimize: !DEV,
splitChunks: {
minSize: 500000,
cacheGroups: {vendors: false,},
},
},
resolve: {
modules: ['node_modules'],
extensions: ['.json', '.js', '.jsx', '.ts', '.tsx', '.less', 'scss'],
},
plugins: [
new HtmlWebpackPlugin({template: path.join(__dirname, '/src/index.html'),
filename: 'app.html',
inject: 'body',
}),
DEBUG && new BundleAnalyzerPlugin(),
new MiniCssExtractPlugin({filename: '[name].css',
chunkFilename: '[name].css',
}),
new ESLintPlugin(),
new ForkTsCheckerWebpackPlugin(),
].filter(Boolean),
}; 总结这篇文章次要记录了开发过程中从我的项目初始化开始,再到一个标准化前端我的项目的搭建途程。波及相干代码标准、开发环境搭建、生产环境优化等,旨在打造出一个可疾速应用的古代 Webpack5.x+React18.x+Typescript+Antd4.x 模板,以供在当前的理论业务场景需要中零老本应用。举荐浏览 1. 研发效力的思考总结 2. 对于技术能力的思考和总结 3. 如何结构化和清晰地进行表白 重磅来袭!2022 上半年阿里云社区最热电子书榜单!千万浏览量、百万下载量、上百本电子书,近 200 位阿里专家参加编写。多元化抉择、全畛域笼罩,汇聚阿里巴巴技术实际精髓,读、学、练一键三连。开发者藏经阁,开发者的工作伴侣~ 点击这里,查看详情。原文链接:https://click.aliyun.com/m/10… 本文为阿里云原创内容,未经容许不得转载。