本文已参与「新人创作礼」活动,一起开启掘金创作之路。
第一步: 初始化项目
创建文件夹webpack_project cd 文件夹 webpack_project 执行命令
npm init
初始化项目得到package.json 文件
第二步:React和TypeScript的结合
1. 先编译一个简单的js 文件
安装webpack的依赖等
webpack
webpack-cli
webpack-dev-server:开发环境启动
webpack-merge:多个配置文件合并,之后对不同环境进行配置的时候会用得到
npm install --save-dev webpack webpack-cli webpack-dev-server webpack-merge
1.1 src文件夹下创建 index.js文件 (ps: 随便写点就行了意思意思🤫)
新建文件夹 src(存放主文件) config (存放配置文件)
路径:src/index.js
const a = 'hello haha'
console.log(a)
1.2 在config文件夹下创建配置文件 webpack.config.js
路径: config/webpack.config.js
// webpack.config.js
const path = require('path')
module.exports = {
target: 'web', // 默认打包成web平台的
mode: 'production', // 环境 development 和 production 环境 链接: https://www.webpackjs.com/concepts/mode/#mode-development
entry: path.resolve(__dirname, '../src/index.js'), // 文件的入口
output: {
filename: 'js/[name].[chunkhash:8].js', // 文件名
path: path.resolve(__dirname, '../dist') // 文件输出地址
}
}
1.3 修改package.json 文件中的 scripts 新增一条运行的命令 用来执行编译index.js 文件
"dev": "webpack --config ./config/webpack.config.js"
// package.json
{
"name": "webpack_project",
"version": "1.0.0",
"description": "antd+ts",
"main": "index.js",
"scripts": {
"dev": "webpack --config ./config/webpack.config.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.0",
"webpack-merge": "^5.8.0"
}
}
1.4 执行代码
npm run dev
得到index.js 编译之后的文件
2. 增加 react 和资源文件的转换等
2.1 安装 react 和 react 路由依赖。修改文件
npm install react react-dom --save-dev
npm install react-router-dom
在src 下新增文件app.jsx
// src/app.jsx
import React from 'react'
export default () => {
return <div>
hello world
</div>
}
更改src/index.js 文件为src/index.jsx 且修改内容为下⬇️
// src/index.jsx
import React from 'react';
import ReactDom from 'react-dom';
import App from './app'
ReactDom.render(<App/>, document.querySelector('#root'))
修改config/webpack.config.js文件中入口改为index.jsx
// config/webpack.config.js
const path = require('path')
module.exports = {
target: 'web',
mode: 'production',
// entry: path.resolve(__dirname, '../src/index.js'),
entry: path.resolve(__dirname, '../src/index.jsx'),
output: {
filename: 'js/[name].[chunkhash:8].js',
path: path.resolve(__dirname, '../dist')
}
}
2.2 增加babel对react 进行转译为js使其能够执行在浏览器中运行
参考链接:
www.cnblogs.com/zhansu/p/13…
juejin.cn/post/684516…
批量增加 proposal 语法支持目前没搞懂作用: juejin.cn/post/684490…
| 依赖 | 说明 | 参考链接 |
|---|---|---|
| babel-loader | webpack的loader插件 | - |
| @babel/core | 核心 | - |
| core-js | corejs版本3 | - |
| @babel/preset-env | 语法转换 | - |
| @babel/preset-react | 转换react | - |
| @babel/polyfill | 补齐api的(未使用) | - |
| babel-plugin-dynamic-import-node | 路由动态加载的 | - |
| @babel/runtime | 集成所有所有语法转换会用到的辅助函数-减少代码打包之后的体积 | - |
| @babel/plugin-transform-runtime | @babel/runtime中辅助函数的自动替换 | - |
| @babel/plugin-transform-arrow-functions | 箭头函数的转换 | - |
| @babel/plugin-syntax-dynamic-import | 用以解析识别import()动态导入语法 | - |
| babel-plugin-import | 按需加载需要的 | - |
| @babel/plugin-proposal-decorators | 装饰器的使用 | |
| @babel/plugin-proposal-class-properties | 支持类属性 | 链接: blog.csdn.net/youlinhuany… |
| @babel/plugin-proposal-private-methods | 私有方法语法的编译 | 链接:www.cnblogs.com/ZheOneAndOn… |
| @babel/plugin-proposal-object-rest-spread | 支持剩余扩展操作符 | - |
| @babel/plugin-syntax-import-meta | 暂时不知道proposal | - |
| @babel/plugin-proposal-function-bind | 暂时不知道proposal | - |
| @babel/plugin-proposal-json-strings | 暂时不知道proposal | - |
| @babel/plugin-proposal-do-expressions | 暂时不知道 | - |
| @babel/plugin-proposal-nullish-coalescing-operator | 暂时不知道 | - |
| @babel/plugin-proposal-optional-chaining | 暂时不知道 | - |
然后呢 我们先安装部分的转译的,其他的之后需要在安装
npm install babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/runtime babel-plugin-dynamic-import-node @babel/plugin-transform-runtime @babel/plugin-transform-arrow-functions @babel/plugin-syntax-dynamic-import @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties @babel/plugin-proposal-private-methods @babel/plugin-proposal-object-rest-spread core-js --save-dev
安装完成之后在项目文件夹中新增.babelrc文件
(PS: 编译的时候默认会全局的查找babel的文件,名称可以为babelrc.js等。也可以不创建文件直接在loader中进行配置,或者是package.json 中添加配置)
// .babelrc
{
"comments": false,
"presets": [
["@babel/env", {
"targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] },
"useBuiltIns": "usage",
"corejs": 3,
"loose": true
}],
"@babel/react"
],
"plugins": [
"dynamic-import-node",
"@babel/plugin-transform-runtime",
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-proposal-object-rest-spread",
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
["@babel/plugin-proposal-private-methods", { "loose": true }]
]
}
添加在文件中config/webpack.config.js 添加转译的loader 和 增加引用文件时可以忽略的后缀引入
// config/webpack.config.js
module.exports = {
//...
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
]
}
]
},
resolve: {
extensions: ['.jsx', '.js', '.css']
}
}
2.3 将打包的好的react 文件放入index.html 中使其在浏览器中运行
在config下新增ejs的html模版 index.ejs(PS: 也可以使用index.html不一定用ejs的,看你自己)
// config/index.ejs
<!DOCTYPE html >
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-store, must-revalidate">
<meta http-equiv="expires" content="Wed, 26 Feb 1997 08:21:57 GMT">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
安装 webpack 插件
html-webpack-plugin:将打包编译好的文件注入到html 中,可以使用自定的模版页可以不使用模版
clean-webpack-plugin:清理 /dist 文件夹
npm install html-webpack-plugin clean-webpack-plugin --save-dev
修改 config/webpack.config.js 配置 新增一下配置
// config/webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin').CleanWebpackPlugin
// ......
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: '项目',
filename: 'index.html',
template: path.resolve(__dirname, './index.ejs'),
hash: true,
cache: false,
inject: true,
minify: {
removeComments: true,
removeAttributeQuotes: true,
collapseWhitespace: true,
minifyJS: true, // 在脚本元素和事件属性中缩小JavaScript(使用UglifyJS)
minifyCSS: true // 缩小CSS样式元素和样式属性
},
nodeModules: path.resolve(__dirname, '../node_modules')
}),
]
在package.json 中的 scripts 新增一条build命令
// package.json
"scripts": {
"dev": "webpack --config ./config/webpack.config.js",
"build": "webpack --config ./config/webpack.config.js"
},
执行命令:
npm run build
在dist 文件夹中可以看到编译出了 index.html 文件和js 文件夹(存放js的)
在浏览器中打开index.html 可以看到代码中的hello world 已经出来了
在这呢 ⬇️
2.4 添加样式的等资源文件的加载
| 插件 | 说明 |
|---|---|
| style-loader | 行内样式转换 |
| css-loader | css样式识别转换 |
| file-loader | 加载图片和字体资源 |
| postcss | postcss-loader | 不同的浏览器的前缀转换: css浏览器的兼容 |
| autoprefixer | 自动编译转译的配合postcss |
| less | less-loader | less的转换:(ps)为之后添加antd的做准备,因为antd使用的less的所以直接就用less了 |
样式: sass stylus 这几种写法目前我没用,加载方式都差不多,看个人喜好
要导入 CSV、TSV 和 XML,你可以使用 csv-loader 和 xml-loader
依赖安装一波
npm install --save-dev style-loader css-loader file-loader postcss postcss-loader less less-loader autoprefixer
在项目中新增postcss的配置文件: postcss.config.js
在编译时会寻找全局的文件自动加上浏览器的前缀(PS: 我习惯用文件进行配置,也可以直接在loader中进行配置postcss-loader)
设置支持哪些浏览器,必须设置支持的浏览器才会自动添加浏览器兼容
// postcss.config.js
module.exports = {
ident: 'postcss',
plugins: [
require('autoprefixer')
]
}
// package.json 中增加支持的浏览器
//...
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
//...
在config/webpack.config.js文件中的module 下 rules新增一下转译
// config/webpack.config.js
{
test: /\.(png|svg|jpg|gif)$/, // 图片
use: [
{
loader: 'file-loader',
options: {
name: 'assets/images/[name].[ext]' // 存放的位置: dist/assets/images/文件
}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/, // 字体
use: [
{
loader: 'file-loader',
options: {
name: 'assets/fonts/[name].[ext]'// 存放的位置: dist/assets/fonts/文件
}
}
]
},
{
test: /\.css$/, // css 样式
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
},
{
test: /\.less$/i, // less 样式
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
在src 下新增app.css 同时 app.jsx修改 引入样式文件
// app.css
.div-container {
margin: 0;
padding: 0;
}
.div-container .div-btn {
width: 100px;
height: 30px;
border-radius: 4px;
text-align: center;
line-height: 30px;
font-size: 14px;
text-align: center;
border: 1px solid #108EE9;
transition: all; // 测试自动加浏览器前缀使用
}
// app.jsx
import React from 'react'
import './app.css'
export default () => {
return <div className='div-container'>
<p>hello world</p>
<div className='div-btn'>跳转</div>
</div>
}
执行命令: npm run build在浏览器中查看index.html 文件,可以看到样式生效且引入成功
但是仔细查看会发现样式被打包进入科main.js 文件中
所以接下来把样式文件拆分一下吧
2.4.1 样式分离
样式分离的话用的这个插件: mini-css-extract-plugin 安装一波
npm install --save-dev mini-css-extract-plugin
然后修改config/webpack.config.js配置
// config/webpack.config.js
// ....
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// ....
plugins: [
//...
new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash:8].css' }), // 设置文件存放的位置和名称
//...
],
module: {
rules: [
//....
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // 'style-loader', 因为我们直接用的 mode是'production' 所以就直接替换了
'css-loader',
'postcss-loader'
]
},
{
test: /\.less$/i,
use: [
MiniCssExtractPlugin.loader, // 'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
// ....
]
},
//...
}
执行一波命令之后,可以看到样式被分离了
2.5 创建开发环境:方便开发调试等
将config/webpack.config.js 文件复制一份,重命名为webpack.dev.js。并且进行调整
// webpack.dev.js
// ...
module.exports = {
target: 'web',
mode: 'development', // 修改为 development
entry: path.resolve(__dirname, '../src/index.jsx'),
output: {
filename: '[name].[hash:8].js', // 修改名称命名采用hash
path: path.resolve(__dirname, '../dist')
},
plugins: [
// new CleanWebpackPlugin(), 可以删除
new MiniCssExtractPlugin({ filename: 'css/[name].css' }), // 修改名称命名
//...
],
module: {
rules: [
//...
,
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
},
{
test: /\.less$/i,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
//...
]
},
//...
devServer: { // 新增webpack-dev-server 的配置
headers: { 'Access-Control-Allow-Origin': '*' },
hot: true, // 热更新
host: '127.0.0.1', // 地址
port: '8081', // 端口
open: true, // 是否自动打开
setupExitSignals: true,
compress: true
}
}
复制更改之后的前后对比
修改package.json中scripts的dev命令为
// "dev": "webpack --config ./config/webpack.config.js" 删除了不要了
"dev": "webpack-dev-server --config ./config/webpack.dev.js",
执行命令
npm run dev
自动打开了浏览器
地址:http://127.0.0.1:8081
开发环境搭建完成了
3. 增加TypeScript
ps: 先增加TypeScript, 接下来全用js写的话然后又把文件转换成ts的写法,太麻烦了
3.1 安装 TS 所需依赖, 同时将配置也替换成ts编译
| 依赖包 | 说明 | 是否用啦 |
|---|---|---|
| typescript | 主要的包 | 使用 |
| ts-node | 用来编译ts文件(ps:使用ts的配置文件的时候需要用) | 使用 |
| @babel/preset-typescript | babel来对ts进行转译 | 使用 |
| ts-loader | ts的转译 | 暂未使用 |
| awesome-typescript-loader | ts的转译 | 暂未使用 |
ps: 因为我使用的是babel来进行转译的js和一些es6的语法的支持等,所以就直接使用@babel/preset-typescript 啦,ts-loader 和 awesome-typescript-loader的话,你们自己看情况用吧 参考的链接
安装一波依赖
npm install --save-dev typescript ts-node @babel/preset-typescript
3.1.1 新增tsconfig.json文件配置
在项目的目录下新建tsconfig.json文件
在tsconfig.json文件中我增加了ts-node的配置项,他的配置和正常的ts配置一样,只是我们在执行使用ts-node执行命令的时候会优先匹配这个配置来解析配置文件, 开发配置文件的配置和项目的配置是不相同的
// tsconfig.json
{
"ts-node": { // 这个是属于ts-node去编译配置文件的时候会执行的配置和开发的配置有所不同
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"sourceMap": true,
"removeComments": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"moduleResolution": "node",
"baseUrl": "./",
"typeRoots": ["node_modules/@types"],
"esModuleInterop": true
},
"exclude": ["node_modules"],
"include": [
"config/*"
]
},
"compilerOptions": {
"target": "es6",
"module": "esnext",
"jsx": "react",
"sourceMap": true,
"removeComments": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"moduleResolution": "node",
"baseUrl": "./",
"typeRoots": ["node_modules/@types"],
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"experimentalDecorators": true
},
"exclude": ["node_modules", "config/*"],
"include": ["src/*"]
}
3.1.2 修改入口文件
app.jsx => app.tsx
index.jsx => index.tsx
修改index.jsx为index.tsx, 修改文件后缀之后引入的依赖会存在红线报错,这个时候提示我们没有找到ts的声明文件,现在就需要安装react 对应的声明文件的依赖
npm i --save-dev @types/react @types/react-dom
3.1.3 修改配置文件
webpack.dev.js => webpack.dev.ts
webpack.config.js => webpack.config.ts
webpack.dev.ts 内容进行如下修改 ⬇️
注意:之前文件中的module.exports 的写法被我替换成了const config: Configuration。写成了TypeScript的写法。因为使用ts来进行配置文件的编写,所有webpack 是无法直接识别ts的语法的,所以使用的是ts-node 来进行ts的转译之后进行node的方式运行的webpack。
// webpack.dev.ts
import path from 'path'
import webpack, { Configuration } from 'webpack'
import WebpackDevServer from 'webpack-dev-server'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import { CleanWebpackPlugin } from 'clean-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
const config: Configuration = {
//...
entry: path.resolve(__dirname, '../src/index.tsx'), // 替换成tsx
//...
module: {
rules: [
//...,
{
test: /\.(ts|tsx)$/,// 替换成ts|tsx
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
]
}
//...
]
},
resolve: {
// 将.js|.jsx 删除 替换成.ts|.tsx新增加 .less
extensions: ['.tsx', '.js', '.ts', '.less', '.css']
}
}
// 之前是直接命令行之行的代码,因为使用ts-node 所以使用ts运行webpack-dev-server
const devserver = new WebpackDevServer({
headers: { 'Access-Control-Allow-Origin': '*' },
hot: true, // 热更新
host: '127.0.0.1', // 地址
port: '8081', // 端口
open: true, // 是否自动打开
setupExitSignals: true,
compress: true
}, webpack(config))
// 启动
devserver.start()
webpack.config.ts 内容进行如下修改 ⬇️
// webpack.config.ts
import path from 'path'
import webpack, { Configuration } from 'webpack'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import { CleanWebpackPlugin } from 'clean-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
const config:Configuration = {
target: 'web',
mode: 'production',
entry: path.resolve(__dirname, '../src/index.tsx'),
//...
module: {
rules: [
//...,
{
test: /\.(ts|tsx)$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
]
}
]
},
resolve: {
extensions: ['.tsx', '.js', '.ts', '.less', '.css']
}
}
// 运行 webpack 和输出日志
webpack(config, (err:any, state:any) => {
if (err) {
console.log(err.stack || err)
} else if (state.hasErrors()) {
let err = ''
state.toString({
chunks: false,
colors: true
}).split(/\r?\n/).forEach((line:any) => {
err += ` ${line}\n`
})
console.warn(err)
} else {
console.log(state.toString({
chunks: false,
colors: true
}))
}
})
webpack.dev.ts 修改前后对比, webpack.config.ts我就不贴代码了
修改完成之后我们就可以直接在设置值的时候,能够知道需要输入的类型啦
3.1.4 在.babelrc 中新增对应的插件
// .babelrc
{
"comments": false,
"presets": [
["@babel/env", {
"targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] },
"useBuiltIns": "usage",
"corejs": 3,
"loose": true
}],
"@babel/react",
"@babel/typescript" // 增加转译
],
//...
}
3.1.5 修改packgae.json 中的命令
修改命令为以下 ⬇️,然后执行命令npm run dev
// packgae.json
"scripts": {
// "dev": "webpack-dev-server --config ./config/webpack.dev.js",
// "build": "webpack --config ./config/webpack.config.js"
"dev": "ts-node ./config/webpack.dev.ts",
"build": "ts-node ./config/webpack.config.ts"
},
运行成功 撒花 🌹💐💐💐💐💐💐
3.2 配置文件优化
写到这之后发现了文件存在很多代码都是可以复用的, 那就优化一波吧,之前安装webpack的时候时候安装了一个 webpack-merge 的依赖,这个时候就能用到啦
首先创建一个公共的配置文件 webpack.web.ts 存放在config/web路径下
⚠️注意入口文件路径和其他的文件路径
// webpack.web.ts
import path from 'path'
import { Configuration } from 'webpack'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
export const ConfigInit = (mode: "development" | "production"):Configuration => {
const isPro:boolean = mode === 'production'
// 样式的数组的
const cssLoaderAry:string[] = [
isPro ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
'postcss-loader'
]
return {
target: 'web',
mode,
entry: path.resolve(__dirname, '../../src/index.tsx'),
output: {
filename: isPro ? 'js/[name].[chunkhash:8].js' : '[name].[hash:8].js',
path: path.resolve(__dirname, '../../dist')
},
plugins: [
new MiniCssExtractPlugin({ filename: isPro ? 'css/[name].[contenthash:8].css' : 'css/[name].css' }),
new HtmlWebpackPlugin({
title: '项目',
filename: 'index.html',
template: path.resolve(__dirname, './index.ejs'),
hash: true,
cache: false,
inject: true,
minify: {
removeComments: true,
removeAttributeQuotes: true,
collapseWhitespace: true,
minifyJS: true, // 在脚本元素和事件属性中缩小JavaScript(使用UglifyJS)
minifyCSS: true // 缩小CSS样式元素和样式属性
},
nodeModules: path.resolve(__dirname, '../../node_modules')
}),
],
module: {
rules: [
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/images/[name].[ext]'
}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/fonts/[name].[ext]'
}
}
]
},
{
test: /\.css$/,
use: cssLoaderAry
},
{
test: /\.less$/i,
use: [
...cssLoaderAry,
'less-loader'
]
},
{
test: /\.(ts|tsx)$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
]
}
]
},
resolve: {
extensions: ['.tsx', '.js', '.ts', '.less', '.css']
}
}
}
修改webpack.dev.ts
// webpack.dev.ts
import webpack, { Configuration } from 'webpack'
import WebpackDevServer from 'webpack-dev-server'
import { ConfigInit } from './web/webpack.web'
// 开发环境的配置文件
const config:Configuration = ConfigInit('development')
const devserver = new WebpackDevServer({
headers: { 'Access-Control-Allow-Origin': '*' },
hot: true, // 热更新
host: '127.0.0.1', // 地址
port: '8081', // 端口
open: true, // 是否自动打开
setupExitSignals: true,
compress: true
}, webpack(config))
devserver.start()
修改webpack.config.ts并重命名为webpack.pro.ts
ps: 注意修改命令中的文件地址
import webpack, { Configuration } from 'webpack'
import { CleanWebpackPlugin } from 'clean-webpack-plugin'
import { merge } from 'webpack-merge' // 文件合并
import { ConfigInit } from './web/webpack.web'
const config:Configuration = merge(ConfigInit('production'), {
plugins: [
new CleanWebpackPlugin()
]
})
webpack(config, (err:any, state:any) => {
if (err) {
console.log(err.stack || err)
} else if (state.hasErrors()) {
let err = ''
state.toString({
chunks: false,
colors: true
}).split(/\r?\n/).forEach((line:any) => {
err += ` ${line}\n`
})
console.warn(err)
} else {
console.log(state.toString({
chunks: false,
colors: true
}))
}
})
修改完成之后如下 ⬇️
注意名称修改了,运行命令的也要修改
4. 项目优化
4.1 ts图片样式的引入
创建资源文件夹src/assets并添加了一个图片
在app.tsx 中引用图片会提示没有相应的声明类型
那就解决吧
在项目目录下创建一个文件夹typings存放一个全局的声明,创建global.d.ts文件 顺便把less 的也声明了
// global.d.ts
declare module '*.less' {
const content: any
export = content
}
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'
declare module '*.pdf'
declare module '*.svg' {
const content: any
export default content
}
修改tsconfig.json 配置文件, 在typeRoots 和 include 中新增typings/*.d.ts
{
//...,
"compilerOptions": {
//...
"typeRoots": ["typings/*.d.ts", "node_modules/@types"],
//...
},
//...
"include": ["src/*", "typings/*.d.ts"]
}
好啦,这下就可以啦。不报错了
运行命令,图片引入成功
将css 修改称less 引入
同样引入成功
4.2 ts文件路径的优化
在目前的项目中我们在引用路径的时候,需要加上(./)或者(../../)多层的这个去找到对应的文件
为了方便和美观,可以在配置中加上这个配置
在webpack.web.ts 中加上以下配置和在tsconfig.json 中加上配置
// webpack.web.ts
//...
resolve: {
alias: {
'@assets': path.resolve(__dirname, '../../src/assets'),
},
extensions: ['.tsx', '.js', '.ts', '.less', '.css']
}
//...
// tsconfig.json
//...
"paths": {
"@assets/*": ["src/assets/*"]
},
//...
然后替换项目中的路径,重启项目,然后OK 🎉撒花
4.3 环境变量的设置
在项目的开发中会判断不同的环境,开发环境 测试环境 正式环境,这就需要一个不同的值来进行判断了
我这边是用到了cross-env这个来区分不同的环境
npm install cross-env --save-dev
在命令中加入tag来区分不同的环境 dev:开发 tes:测试 pro:正式
"scripts": {
"dev": "cross-env tag=dev ts-node ./config/webpack.dev.ts",
"build:tes": "cross-env tag=tes ts-node ./config/webpack.pro.ts",
"build:pro": "cross-env tag=pro ts-node ./config/webpack.pro.ts"
},
修改配置文件webpack.web.ts 和 typings/global.d.ts新增配置
// webpack.web.ts
//...
plugins: [
//...
new DefinePlugin({
'process.env': {
tag: JSON.stringify(process.env.tag),
version: JSON.stringify(packages.version)
}
}),
],
//...
// typings/global.d.ts
//...
declare module 'process' {
global {
namespace NodeJS {
export interface ProcessEnv {
tag: 'dev' | 'tes' | 'pro'
version: string
}
}
}
}
测试输出 可以啦
4.4 代码压缩优化等
依赖包 ⬇️
| 依赖 | 说明 |
|---|---|
| terser-webpack-plugin | 用于处理 js 的压缩和混淆 |
| css-minimizer-webpack-plugin | 压缩css文件 |
| compression-webpack-plugin | 预先准备的资源压缩版本,使用 Content-Encoding 提供访问服务 |
| copy-webpack-plugin | 复制静态的文件: 存在文件才能复制 |
npm install terser-webpack-plugin css-minimizer-webpack-plugin compression-webpack-plugin copy-webpack-plugin --save-dev
修改webpack.pro.ts 文件 简单的配置一波压缩优化
import webpack, { Configuration, BannerPlugin, LoaderOptionsPlugin } from 'webpack'
import { CleanWebpackPlugin } from 'clean-webpack-plugin'
// import CopyWebpackPlugin from 'copy-webpack-plugin' // 复制静态资源使用
import CompressionWebpackPlugin from 'compression-webpack-plugin'
import TerserPlugin from 'terser-webpack-plugin'
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
import { merge } from 'webpack-merge'
import { ConfigInit } from './web/webpack.web'
const config:Configuration = merge(ConfigInit('production'), {
plugins: [
new CleanWebpackPlugin(),
new CompressionWebpackPlugin(),
// new CopyWebpackPlugin({
// patterns: [{ from: 'statics', to: 'statics' }]
// }),
new LoaderOptionsPlugin({
minimize: true
}),
new BannerPlugin('版权所有,翻版必究')
],
optimization: {
splitChunks: {
chunks: 'all'
},
runtimeChunk: {
name: 'mainifels'
},
minimize: true,
minimizer: [
new TerserPlugin(),
new CssMinimizerPlugin()
]
},
performance: {
hints: false,
maxAssetSize: 4000000, // 整数类型(以字节为单位)
maxEntrypointSize: 5000000 // 整数类型(以字节为单位)
}
})
//....省略
4.5 开发环境浏览器打开在同一个tab页打开
运行命令的时候重启一次打开一个tab 页很烦,所以呢优化一下
参考:create-react-app 的启动方式
复制出这两个文件也可看当前项目的源码
openBrowser.js
openChrome.applescript
修改配置文件webpack.dev.ts如下 ⬇️
import webpack, { Configuration } from 'webpack'
import WebpackDevServer from 'webpack-dev-server'
import { ConfigInit } from './web/webpack.web'
const openBrowser = require('./util/openBrowser')
// 开发环境的配置文件
const config:Configuration = ConfigInit('development')
const host:string = '127.0.0.1'
const port:string = '8081'
const devserver = new WebpackDevServer({
headers: { 'Access-Control-Allow-Origin': '*' },
hot: true, // 热更新
host: host, // 地址
port: port, // 端口
// open: true, // 关闭
setupExitSignals: true,
compress: true
}, webpack(config))
devserver.start().then(() => {
// 启动界面
openBrowser(`http://${host}:${port}`)
})
记得关闭webpack-dev-server的配置中的自动打开 open: false 或者注释
在ts中引用js 采用的require的形式
运行命令:npm run dev
完结撒花 🌹🌹
4.6 构建优化
在项目越做越大的时候,项目文件太多,在打包编译时,运行会越来越慢
- 提前打包需要编译的包, 分离第三方的包(DllPlugin 和 DllReferencePlugin)
- 编译使用多线程进行编译(webpack5之前的可以使用happypack进行优化,但是webpack5就不行了,官方不在维护happypack,推荐使用: thread-loader)
4.6.1 提前打包需要编译的包
安装依赖
| 依赖 | 说明 |
|---|---|
| add-asset-html-webpack-plugin | 向html中插入指定的script |
npm install add-asset-html-webpack-plugin --save-dev
新增配置文件webpack.dll.ts对第三方的包进行抽离
DllPlugin 在当前配置文件中使用,打包好的文件输出在config/dll目录下
import path from 'path'
import webpack, { Configuration, DllPlugin } from 'webpack'
import { CleanWebpackPlugin } from 'clean-webpack-plugin'
export const WebpackDllConfig:Configuration = {
target: 'web',
mode: 'production',
entry: {
reactrouterdom: 'react-router-dom',
react: 'react',
reactdom: 'react-dom'
},
output: {
path: path.resolve(__dirname, 'dll'),
publicPath: './',
filename: '[name].js',
library: '[name]_library_wcr'
},
plugins: [
new CleanWebpackPlugin(),
new DllPlugin({
path: path.join(__dirname, 'dll', '[name].manifest.json'),
name: '[name]_library_wcr'
})
]
}
webpack(WebpackDllConfig, (err:any, state:any) => {
if (err) {
console.log(err.stack || err)
} else if (state.hasErrors()) {
let err = ''
state.toString({
chunks: false,
colors: true
}).split(/\r?\n/).forEach((line:any) => {
err += ` ${line}\n`
})
console.warn(err)
} else {
console.log(state.toString({
chunks: false,
colors: true
}))
}
})
增加命令:build:dll
"scripts": {
//...
"build:dll": "ts-node ./config/webpack.dll.ts",
//...
},
修改webpack.pro.ts配置
import path from 'path'
import webpack, { Configuration, BannerPlugin, LoaderOptionsPlugin, DllReferencePlugin } from 'webpack'
//...
const WebpackDllConfig = require('./webpack.dll').WebpackDllConfig
// 提前打包好的信息 进行引用,循环遍历设置
const dllKey:string[] = Object.keys(WebpackDllConfig.entry)
const dllPluginsAry:any[] = [] // DllReferencePlugin数组
const AddAssetHtmlPluginAry:any[] = [] // AddAssetHtmlPlugin 插入的数组
for (let index = 0; index < dllKey.length; index++) {
const key = dllKey[index]
AddAssetHtmlPluginAry.push({
filepath: path.resolve(__dirname, `./dll/${key}.js`),
publicPath: './dll',
outputPath: 'dll'
})
dllPluginsAry.push(new DllReferencePlugin({
manifest: require(path.join(__dirname, './dll/', `${key}.manifest.json`))
}))
}
const config:Configuration = merge(ConfigInit('production'), {
plugins: [
//...
...dllPluginsAry, // DllReferencePlugin的使用
// 插入指定的js
new AddAssetHtmlPlugin(AddAssetHtmlPluginAry),
//...
],
//...
})
//...
配置完成之后如图, 注意配置的路径和AddAssetHtmlPlugin 输出的路径
注意先执行分离打包在进行编译
npm run build:dll
npm run build:tes
结果如下 ⬇️
4.6.2 多进程编译
安装依赖
| 依赖 | 说明 |
|---|---|
| thread-loader | 多进程进行编译, 放在耗时的 loader之前 |
npm install --save-dev thread-loader
第三步 增加antd
antd 参考链接
1. 安装依赖
| 依赖 | 说明 |
|---|---|
| babel-plugin-import | 按需加载使用的 |
| antd | 主要的 |
npm install antd
npm install babel-plugin-import --save-dev
2. 修改config/web/webpack.web.ts下的配置
//...
{
test: /\.less$/i,
use: [
...cssLoaderAry,
{
loader: 'less-loader',
options: {
lessOptions: {
exclude: /node_modules/,
// modifyVars: theme, // 自定义主题的
javascriptEnabled: true
}
}
}
]
},
//...
3. 修改.babelrc文件
{
"comments": false,
"presets": [
["@babel/env", {
"targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] },
"useBuiltIns": "usage",
"corejs": 3,
"loose": true
}],
"@babel/react"
],
"plugins": [
["import", { "libraryName": "antd", "style": "css" }, "antd"], // 新增这个
"dynamic-import-node",
"@babel/plugin-transform-runtime",
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-proposal-object-rest-spread",
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
["@babel/plugin-proposal-private-methods", { "loose": true }]
]
}
4. 在app.tsx 中引入 完成
第四步 代码检查和规范
为了规范代码的格式 eslint 和 tslint 这个有空在更新吧🥱
结束
第一次写,写得有遗漏的请指出。不明白的或者有问题欢迎指出👏
项目地址: Github 地址