前言
本文详细的介绍如何从头开始搭建一个react项目,帮助新人了解项目搭建流程和打包流程。
需要注意包版本问题,不同版本代表着不同的代码。如果包版本不同启动项目时可能会出现未知错误。
目录说明
└─config #webpack配置文件
│ webpack.build.config.js #webpack生产配置文件
│ webpack.dev.config.js #webpack开发配置文件
│ dist #打包后的文件
│ node_modules #npm下载的包文件
└─server #json-server 的数据来源
│ db.json #数据
└─src #项目源码
│ images #图片文件
├─pages #页面目录
│ home.css #样式文件
│ Home.js #页面组件
│ home.less
│ Page1.jsx
│ Page2.tsx #ts 文件
│ Thunk.js
├─store #redux store配置
│ action.js
│ actionsUser.js
│ reducer.js
├─types
│ types.d.ts #自定义 ts类型
│ index.html
│ index.js #入口文件
│ router.js #路由文件
│ .gitignore #git 上传过滤
│ babel.config.json #babel 配置文件 配置插件使用
│ package-lock.json #包版本
│ package.json #npm 配置文件
│ postcss.config.js #postcss 配置文件
│ README.md #简介
│ tsconfig.json #ts配置检查文件
node
Node.js
是能够在服务器端运行 JavaScript 的开放源代码、跨平台 JavaScript 运行环境。
安装node教程
版本测试 --通过命令行测试版本号
npm
npm
(全称 Node Package Manager,即“node包管理器”)是Node.js默认的、用JavaScript编写的软件包管理系统。NPM 使用介绍
如网速不好时可以使用淘宝npm
镜像,安装成功后只需要把npm
修改为cnpm
执行就可以了。
npm install cnpm -g --registry=https://registry.npm.taobao.org
创建项目
创建项目根目录。可通过命令行也可自己创建文件夹。
mkdir react-obj
初始化项目,后续命令都是进入根目录后执行。(可一直回车生)
npm init // 按照提示填写项目基本信息 生成 package.json 文件
webpack
安装webpack
,webpack
是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。
npm install --save-dev webpack
npm install --save-dev webpack@<version> // 安装指定版本
npm install --save-dev webpack-cli
npm
命令简写
install 安装命令 简写:i
--save-dev 开发时要依赖的东西 简写:-D
--save 发布时要依赖的东西 简写:-S
-g 全局安装包(加载命令最后)
注意:在webpack4之后 webpack-cli 和 webpack 就分为两个包
基础的配置 webpack文档,
新建config目录放置webpack开发配置webpack.dev.config.js
mkdir config
cd config
echo. > webpack.dev.config.js // window 创建文件
// webpack.dev.config.js
const path = require('path');
module.exports = {
/* 入口 */
entry: path.join(__dirname, '../src/index.js'),
/* 输出到dist目录,输出文件名字为bundle.js */
output: {
path: path.join(__dirname, '../dist'),
filename: 'bundle.js'
}
};
新建index.js
入口文件
mkdir src
cd src
echo. > index.js
// index.js
document.getElementById('root').innerHTML = "Hello React";
修改 package.json
文件中 "scripts" 的值
{
"name": "react-obj",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --config ./config/webpack.dev.config.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.11.0",
"webpack-cli": "^4.2.0"
}
}
执行打包命令
npm run build
- 全局安装 webpack 和 webpack-cli
- 也可以直接执行打包命令
webpack --config ./config/webpack.dev.config.js
执行完成后可以看到生成了dist文件夹和bundle.js
。内容就是index.js
中的内容表示打包成功。
Babel
Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。Babel文档
- @babel/core 调用Babel的API进行转码
- @babel/preset-env 用于解析 ES6+
- @babel/preset-react 用于解析 JSX
- babel-loader 加载器
npm i @babel/core @babel/preset-env @babel/preset-react babel-loader -D
在根目录添加babel的配置文件 babel.config.json
{
"presets": ["@babel/preset-react", "@babel/preset-env"],
"plugins": []
}
修改 webpack.dev.config.js
文件
module.exports = {
...
/* cacheDirectory是用来缓存编译结果,下次编译加速 */
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: ["babel-loader?cacheDirectory=true"],
include: path.join(__dirname, "../src"),
},
],
}
};
修改 src/index.js
var tem = str => {
document.getElementById('root').innerHTML = str;
};
tem('我现在在使用ES6!');
在dist文件夹下创建index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="./bundle.js"></script>
</body>
</html>
执行打包命令
npm run build
用浏览器打开index.html
文件看到 我现在在使用ES6! 打包成功。
React
npm i react react-dom -S
修改 src/index.js
import React from 'react';
import ReactDom from 'react-dom';
class Hello extends React.Component {
render() {
return (
<div>
开始使用React!
</div>
)
}
}
ReactDom.render(<Hello />, document.getElementById('root'));
执行打包命令
npm run build
打开inde.html文件 看到 开始使用! 打包成功.
react-router
在 Web 前端单页应用 SPA(Single Page Application)中,路由描述的是 URL 与 UI (--react组件) 之间的映射关系,这种映射是单向的,即 URL 变化引起 UI 更新(无需刷新页面)。
react-router 也是使用javascript自带的 hash
和 history
api实现的。
npm i react-router-dom -S
现在开始使用路由。在src下创建文件夹pages
和文件router.js
,创建文件Home.js
、Page1.js
、Page2.js
。
cd src
mkdir pages
echo. > router.js
cd pages
echo. > Home.js
echo. > Page1.js
echo. > Page2.js
// src/psges/Home.js
import React from 'react';
export default class Hello extends React.Component {
render() {
return (
<div>
我是首页
</div>
)
}
}
// src/psges/Page1.js
import React from "react";
export default function () {
return <div>我是页面1</div>;
}
// src/psges/Page2.js
import React from "react";
export default function () {
return <div>我是页面2</div>;
}
// src/router.js
import React from "react";
// 引入路由组件
import { BrowserRouter, Route, Switch, Link } from "react-router-dom";
import Home from "./pages/Home";
import Page1 from "./pages/Page1";
import Page2 from "./pages/Page2";
// 添加404页面
function NotFound(){
return <div>404</div>;
}
export default function () {
return (
<BrowserRouter>
{/* 开始使用路由 */}
<div>
<div>
{/* 通过 Link 修改浏览器的url Switch组件 通过url 来判断展示那个Route 包裹的组件页面 */}
<Link to="/">首页</Link>
</div>
<div>
<Link to="/page1">Page1</Link><br></br>
<Link to="/page2">page2</Link>
</div>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/page1" component={Page1} />
<Route path="/page2" component={Page2} />
{/* 路由找不到 加载404组件 */}
<Route component={NotFound} />
</Switch>
</div>
</BrowserRouter>
);
}
// 修改 src/index.js
import React from 'react';
import ReactDom from 'react-dom';
import Root from './router';
ReactDom.render(<Root />, document.getElementById('root'));
现在执行打包命令,打开index.html
看到页面能正常输出。但是路由是不能跳转的,因为现在是根目录打开无法跳转路由。
需要安装 webpack-dev-server
,是webpack官方提供的一个小型Express服务器。
npm i webpack-dev-server -D
// 修改 package.json
"scripts": {
"start": "webpack-dev-server --config ./config/webpack.dev.config.js",
"build": "webpack --config ./config/webpack.dev.config.js"
},
//在 `webpack.dev.config.js` 添加配置
module.exports = {
...
devServer: {
contentBase: path.join(__dirname, '../dist'),
compress: true, // gzip压缩
host: '0.0.0.0', // 允许ip访问
hot:true, // 热更新
historyApiFallback:true, // 解决启动后刷新404
port: 8000 // 端口
},
};
--这里就遇到了包版本问题 前面安装webpack-cli@4
版本 和 webpack-dev-server@3
版本 是不兼容的 启动就会报错。这里把webpack-cli
降低了版本。
npm uninstall webpack-cli -D
npm i webpack-cli@3 -D
启动服务,打开http://localhost:8000 就能开始使用路由了
npm start
devtool
在开发时需要定位错误,打包后的代码比较难以理解,这时候只需要在webpack.dev.config.js
添加devtool
就能在错误的时候定位到自己写的代码。
devtool: 'inline-source-map',
React-Redux
Redux 其实就是找个,不能直接操作的对象存放一些共享数据。只能通过特殊的方式来获取和修改数据。我们这里使用的React-Redux其实就是对Redux进行的一次封装,把那个对象放在了props上。
npm i redux react-redux -S
下面我们来吧Home.js
修改为使用react-redux
的计数器。
// Home.js
import React from "react";
// 引入 react-redux 中的高阶函数 绑定获取和修改的特殊方法
import { connect } from 'react-redux';
// 定义 store的修改类型
import { addAction, reduceAction } from "../store/action";
function Home(props) {
const { count, onAddClick, onReduceClick } = props;
return (
<div>
<span>我是首页计数器{count}</span>
<button onClick={onAddClick}>加</button>
<button onClick={onReduceClick}>减</button>
</div>
);
}
// 获取store中的值到当前组件
function mapStateToProps(state) {
return {
count: state.count,
};
}
// 修改store中的值
function mapDispatchToProps(dispatch) {
// 获取action.js 中定义的特殊类型
return {
onAddClick: () => dispatch(addAction),
onReduceClick: () => dispatch(reduceAction),
};
}
// connect接收两个参数 叫什么都行
// 第一个参数获取 reducer.js 中 counter 定义的state的值
// 第二个参数通过 回调 dispatch 传入指定类型 来修改 reducer.js 中 counter 定义的state
export default connect(mapStateToProps, mapDispatchToProps)(Home);
在根目录创建store
文件夹,在其中创建action.js
、reducer.js
cd src
mkdir store
cd store
echo. > action.js
echo. > reducer.js
// store/reducer.js 创建redux中的store对象,设置修改对象的类型。
import { createStore } from 'redux';
function counter(state = { count: 0 }, action) {
const count = state.count;
switch (action.type) {
case "add":// 约定的类型
return { count: count + 1 };
case "reduce":
return { count: count - 1 };
default:
return state;
}
}
// 创建 Store
const store = createStore(counter)
// 导出要使用的Store
export default store
// store/action.js
// 根据reducer.js 设置的约定类型 定义修改store的 公用action对象
const addAction = { type: 'add' }
const reduceAction = { type: 'reduce' }
export { addAction, reduceAction}
// 修改router.js
// 配置store对象 放入组件最顶级的props中
...
import { Provider } from 'react-redux'
import Home from "./pages/Home";
import Page1 from "./pages/Page1";
import Page2 from "./pages/Page2";
import store from "./store/reducer";// 引入store
...
export default function () {
return (
<Provider store={store}>
{/* 把store放入props中让所有子组件都能获取到 */}
...
</Provider>
);
}
// 修改pages/Page1.js 测试 --当在其他组件中修改store的值后 每个组件对应的值都修改了
import React from "react";
import { connect } from 'react-redux'
function Home(props) {
const { count } = props;
return <div>我是页面1--{count}</div>;
}
// 获取store中的值
export default connect((state)=>({count: state.count}))(Home);
启动项目,当Home.js
中修改计数值,切换路由到Page1.js
计数值跟着修改。这样Redux就创建成功了。
在这里梳理一下我们做了什么。
1.通过 reducer 创建我们需要的store对象和修改store对象的类型,这里可以创建多个reducer合并在一起。
2.通过 action 设置公用的约定类型对象。
3.通过 Provider 把store 放入顶部props。
4.通过 connect 第一个参数获取store的值,通过第二个参数传入action对象修改store。
集成TypeScript
npm i typescript ts-loader -D
typescript 基础安装包
ts-loader 解析.ts文件 为 es5的.js文件
修改package.json
的scripts
,然后 npm run tsc
初始化 TypeScript
,生成 tsconfig.json
文件,管理工程配置,例如你想包含哪些文件和进行哪些检查。
"scripts": {
...
"tsc": "tsc --init"
},
在webpack.dev.config.js
的 module -> rules 中添加
module: {
rules: [
...
{
test:/\.(ts|tsx)?$/,
use:'ts-loader',
include: path.join(__dirname, "../src"),
exclude: /node_modules/
},
],
},
// webpack.dev.config.js 中添加
// import导入文件 无需后缀配置
module.exports = {
...
resolve: {
extensions: [".js", ".jsx", ".json",".ts",".tsx"]
},
}
修改 Page2.js
为 Page2.tsx
,发现文件报错。
安装 react
的 @types/react
获取ts类型,修改tsconfig.json
添加 "jsx": "react"
设置jsx验证。
当遇见框架没有@types包时在src
下创建types -> types.d.ts
在文件中自定义类型。
npm i @types/react -D
// tsconfig.json
{
"compilerOptions": {
"jsx": "react",
...
},
"include": [ // 所需要用ts的文件
"src/**/*",
],
}
cd src
mkdir types
cd types
echo. > types.d.ts
// types.d.ts
declare module "react-redux" {
export {}
}
// Page2.tsx
import React from "react";
// 使用ts设置 强类型
interface pageProps{
num?:number,
text:string,
}
function Test(props:pageProps){
return <div>{props.text}{props.num}</div>;
}
export default function () {
return <Test text="使用Ts。你好我是页面" num={2}/>;
}
重新启动页面正常加载,配置成功,就可以正常使用ts了。
添加css
npm i css-loader style-loader -D
css-loader 解析类似@import 和 url(...)等方法实现。
style-loader 解析css文件,打包后加入页面。
在webpack.dev.config.js
的 module -> rules 中添加
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
在pages中创建 home.css
echo. > home.css
// home.css
.home {
border: 1px solid rgb(108, 92, 153);
display: flex;
}
// 修改`Home.js`
...
import "./home.css"
function Home(props) {
...
return (
<div className="home">
...
</div>
);
}
...
集成PostCSS优化
集成PostCSS 后,可以自动给css属性加浏览器前缀。
postcss-cssnext
允许你使用未来的 CSS 特性(包括 autoprefixer)。
npm i postcss-loader postcss-cssnext -D
修改webpack.dev.config.js
中 module -> rules
{
test: /\.css$/,
use: ["style-loader", "css-loader","postcss-loader"],
},
然后在根目录下新建postcss.config.js
module.exports = {
plugins: {
'postcss-cssnext': {}
}
};
启动服务npm start
查看css是否加上前缀,加上表示成功。
添加less
npm i less less-loader -D
less 基础安装包
less-loade 把.less解析为.css文件
在webpack.dev.config.js
的 module -> rules 中添加
{
test: /\.less$/,
use: ["style-loader","css-loader",
{
loader: "less-loader",
options: {
lessOptions: {
strictMath: true,
},
},
},
"postcss-loader",
],
},
在pages中创建 home.less
echo. > home.less
.home {
.hLess{
color: #379adb;
}
}
// 修改`Home.js`
...
import "./home.less";
function Home(props) {
...
return (
<div className="home">
<span className="hLess">--我是首页计数器{count}</span>
...
</div>
);
}
...
启动服务npm start
看见 我是首页计数器 改变颜色表示加载成功。
添加图片
npm i url-loader file-loader -D
在src中添加images文件夹放入图片,修改Home.js
cd src
mkdir images
// Home.js
...
import timg from "../images/timg.jpg"
function Home(props) {
...
return (
<div className="home">
...
<img src={timg} />
</div>
);
}
...
webpack.dev.config.js
module -> rules 中添加
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: "url-loader",
options: {
// 控制小于10K的图片会被转成base64编码,直接插入HTML中.
limit: 10240,
},
},
],
},
添加Ajax请求
现在没有后端服务,我们使用 json-server 来模拟后端服务,然后使用axios发起ajax请求。
npm i json-server -D
npm i axios -S
创建server文件夹,在里面创建db.json
mkdir server
cd server
echo. > db.json
{
"users": [
{
"name": "a",
"id": 1
},
{
"name": "b",
"id": 2
}
]
}
修改package.json
中 scripts
"server":"json-server server/db.json --watch --port 3000"
使用时需要分别启动两个服务,启动json-server
后可直接查看数据。
因为启用了两个服务发送请求时需要跨域,需要设置代理proxy
修改webpack.dev.config.js
devServer: {
...
proxy: { // 配置服务代理
'/api': {
target: 'http://localhost:3000',
// 转换请求 /api/users 为 http://localhost:3000/users
pathRewrite: {'^/api' : ''},
changeOrigin: true
}
},
},
修改Home.js
import React,{useEffect,useState} from "react";
// 引入 react-redux 中的高阶函数 绑定获取和修改的特殊方法
import { connect } from "react-redux";
import axios from "axios";
// 定义 store的修改类型
import { addAction, reduceAction } from "../store/action";
import "./home.css";
import timg from "../images/timg.jpg";
function Home(props) {
const { count, onAddClick, onReduceClick } = props;
// 使用HOOK state
const [state,setState] = useState([]);
// 使用HOOK 副作用函数 --第二个参数为空表示 子执行一次
// 有值 表示对应的值修改 就在执行一次
useEffect(()=>{
// 发送请求
axios.get("/api/users").then((res) => {
let data = JSON.parse(res.request.responseText);
setState(data)
});
},[])
return (
<>
<div className="home">
<span className="hLess">{state[0]?.name}--我是首页计数器{count}</span>
<button onClick={onAddClick}>加</button>
<button onClick={onReduceClick}>减</button>
</div>
<img src={timg} />
</>
);
}
// 获取store中的值到当前组件
function mapStateToProps(state) {
return {
count: state.count,
};
}
// 修改store中的值
function mapDispatchToProps(dispatch) {
// 获取action.js 中定义的特殊类型
return {
onAddClick: () => dispatch(addAction),
onReduceClick: () => dispatch(reduceAction),
};
}
// connect接收两个参数 叫什么都行
// 第一个参数获取 reducer.js 中 counter 定义的state的值
// 第二个参数通过 回调 dispatch 传入指定类型 来修改 reducer.js 中 counter 定义的state
export default connect(mapStateToProps, mapDispatchToProps)(Home);
分别启动两个服务
npm start
npm run server
redux-thunk中间件使用
在使用redux
中需要通过请求获取数据修改store
对象。但是redux
的action必须是个对象不能直接传入函数。所以需要使用中间件来实现异步修改数据。
npm i redux-thunk -S
先创建user数据的reducer、action 和 测试页面。
// store/actionsUser.js
import axios from 'axios';
export const INFO = "user/INFO";
// 异步修改 store 的 state
// connect函数 中的第二个参数需要的值
export function getUser() {
return dispatch => {
axios.get('/api/users').then((res)=>{
let data = JSON.parse(res.request.responseText);
dispatch({
type: INFO,
payload: data
});
})
}
}
// 修改 store/reducer.js
import { createStore,applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { INFO } from "./actionsUser";// 获取约定类型
// 计数器
function counter(state = { count: 0 }, action) {
switch (action?.type) {
case "add":
return { ...state,count: state.count + 1 };
case "reduce":
return { ...state,count: state.count - 1 };
default:
return state;
}
}
// 异步修改
function reducer(state = { user:{} }, action) {
switch (action?.type) {
case INFO:
return {
...state,
user: action.payload,
};
default:
return state;
}
}
// 合并 reducer
function combineReducers(state = {}, action) {
return {
counter: counter(state.counter, action),
reducer: reducer(state.user, action)
}
}
// 创建store 对象
const store = createStore(combineReducers,applyMiddleware(thunkMiddleware));
// 导出要使用的Store
export default store
创建 pages/Thunk.js
import React from "react";
// 引入 react-redux 中的高阶函数 绑定获取和修改的特殊方法
import { connect } from "react-redux";
// 定义 store的修改类型
import { getUser } from "../store/actionsUser";
function Home(props) {
const { user = [], getUser} = props;
return (
<div>
<span>我是异步修改数据{user[0]?.name}</span>
<button onClick={getUser}>修改</button>
</div>
);
}
// 获取store中的值到当前组件
function mapStateToProps(state) {
return {
user: state.reducer.user,
};
}
export default connect(mapStateToProps, {getUser})(Home);
因为reducer.js
修改,修改Home.js
、Page1.js
获取正常数据。
...
// 获取store中的值到当前组件
function mapStateToProps(state) {
return {
count: state.counter.count,
};
}
...
...
// 获取store中的值
export default connect((state)=>({count: state.counter.count,}))(Home);
最后在router.js
中添加组件路由。
...
import Thunk from "./pages/Thunk.js";
...
return (
...
<div>
<Link to="/thunk">thunk页</Link>
</div>
<Switch>
...
<Route exact path="/thunk" component={Thunk} />
</Switch>
...
)
npm start
重新启动,进入路由/thunk
点击修改,能获取到数据,表示异步加载数据成功。
缓存优化
浏览器自带文件缓存,当第一次加载了bundle.js
,第二次就不会下载这个文件,需要客户清空本地缓存后才会重新下载。
所以我们生成文件时都生成不同的名字。修改webpack.dev.config.js
的 output。 filename
中自动生成文件的参数。
module.exports = {
...
output: {
path: path.join(__dirname, "../dist"),
filename: '[name].[chunkhash].js',
}
...
}
提取公共包
我们通过npm下载的公共库中的代码是不会修改的,所以不需要打包在bundle.js
中,需要我们提取为公共代码。
webpack4
之前使用的CommonsChunkPlugin
插件,在webpack4
之后改为optimization.splitChunks。
module.exports = {
...
// 优化项配置
optimization: {
// 分割代码块
splitChunks: {
// -默认作用于异步chunk,值为all/initial/async/function(chunk),
// -值为function时第一个参数为遍历所有入口chunk时的chunk模块,
// -chunk._modules为chunk所有依赖的模块,通过chunk的名字和所有依赖模块的resource可以自由配置,
// -会抽取所有满足条件chunk的公有模块,以及模块的所有依赖模块,包括css
// chunks: "all"
// 设置缓存组用来抽取满足不同规则的chunk
cacheGroups: {
// 抽取第三方模块
vendor: {
name: 'vendor',// 模块名
test: /node_modules/, // 如果你多次引用了node_modules第三方模块,就抽取出来
chunks: "all"
}
}
}
},
...
}
HtmlWebpackPlugin优化
npm i html-webpack-plugin -D
每次自动把js插入到模板index.html
里面。新建src/index.html
,把dist文件夹删除。
cd src
echo. > index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
修改webpack.dev.config.js
,增加plugin
...
var HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
template: path.join(__dirname, "../src/index.html"),
}),
],
...
}
重新运行npm start
如果能正常访问就表示成功。
文件压缩
使用uglifyjs-webpack-plugin
来压缩文件,使打包出来的文件体积更小。
npm i uglifyjs-webpack-plugin -D
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
plugins: [
new UglifyJSPlugin()
]
}
在webpack4+
中添加了 模式(mode) 设置模式为production
会自动安装这个插件
module.exports = {
mode:"production"
}
清空上一次的打包文件
webpack
每次打包,都会生成新的文件放入dist文件夹下,我们可以使用clean-webpack-plugin
在打包前清除上一次的文件。
npm i clean-webpack-plugin -D
// 清空上一次的打包文件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
...
plugins: [
...
new CleanWebpackPlugin()
],
...
生产坏境构建
开发项目和打包发版时使用webpack
的插件是不一样的。
所以我们创建一个和 webpack.dev.config.js
内容一样的 webpack.build.config.js
,根据不同环境修改配置。
// package.json 修改 build 时调用 webpack.build.config.js
"scripts": {
"start": "webpack-dev-server --config ./config/webpack.dev.config.js",
"build": "webpack --config ./config/webpack.build.config.js",
"server": "json-server server/db.json --watch --port 3000",
"tsc": "tsc --init"
},
// 删除开发时才需要的配置
const path = require("path");
// html自动打包
var HtmlWebpackPlugin = require("html-webpack-plugin");
// 清空上一次的打包文件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
// 模式 开始模式 会自动预设安装插件 模式不同安装插件不同
// 可以使用 node 自带的 process.env.NODE_ENV 来获取所在的环境
mode: 'production',// production 生产模式 development 开发模式
/* 入口 */
entry: path.join(__dirname, "../src/index.js"),
/* 输出到dist目录,输出文件名字为bundle.js */
output: {
path: path.join(__dirname, "../dist"),
filename: '[name].[chunkhash].js',
},
// 加载打包前的代码
devtool: "cheap-module-source-map",
/* cacheDirectory是用来缓存编译结果,下次编译加速 */
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: ["babel-loader?cacheDirectory=true"],
include: path.join(__dirname, "../src"),
},
{
test:/\.(ts|tsx)?$/,
use:'ts-loader',
include: path.join(__dirname, "../src"),
exclude: /node_modules/
},
{
test: /\.css$/,
use: ["style-loader", "css-loader","postcss-loader"],
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: "url-loader",
options: {
// 控制小于10K的图片会被转成base64编码,直接插入HTML中.
limit: 10240,
},
},
],
},
{
test: /\.less$/,
use: ["style-loader","css-loader",
{
loader: "less-loader",
options: {
lessOptions: {
strictMath: true,
},
},
},
"postcss-loader",
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
/* html压缩 */
minify: true,
template: path.join(__dirname, "../src/index.html"),
}),
new CleanWebpackPlugin()
],
// 优化项配置
optimization: {
// 分割代码块
splitChunks: {
// -默认作用于异步chunk,值为all/initial/async/function(chunk),
// -值为function时第一个参数为遍历所有入口chunk时的chunk模块,
// -chunk._modules为chunk所有依赖的模块,通过chunk的名字和所有依赖模块的resource可以自由配置,
// -会抽取所有满足条件chunk的公有模块,以及模块的所有依赖模块,包括css
// chunks: "all"
// 设置缓存组用来抽取满足不同规则的chunk
cacheGroups: {
// 抽取第三方模块
vendor: {
name: 'vendor',// 模块名
test: /node_modules/, // 如果你多次引用了node_modules第三方模块,就抽取出来
chunks: "all"
}
}
}
},
// 导入文件 无需后缀
resolve: {
extensions: [".js", ".jsx", ".json",".ts",".tsx"]
},
};
参考资料
菜鸟教程
Babel教程
webpackjs教程
Web 前端路由原理解析和实现
Redux 入门教程
TypeScript教程
搭建React全家桶框架教程
源码地址: github.com/nie-ny/reac…
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!