其实主要是想复习webpack
从HTML JavaScript开始
初始化
mkdir react-linkstart && cd $_
git init
pnpm init
echo "node_modules/\ndist/" > .gitignore
touch index.html
mkdir src && cd $_ && echo "console.log('Hello world!')" > index.js
cd .. && mkdir dist
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React Link Start</title>
</head>
<body>
<h1>Hello world</h1>
</body>
</html>
当前文件结构
.
├── .gitignore
├── index.html
├── package.json
├── dist
└── src
└── index.js
安装webpack
pnpm i webpack webpack-cli webpack-dev-server html-webpack-plugin -D
package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack serve --config ./webpack.config.js"
},
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
entry: './src/index.js',
devServer: {
port: 3000,
hot: true,
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'index.html'),
filename: 'index.html',
})
],
}
运行一下pnpm run dev
配置babel
安装babel
pnpm i babel-loader @babel/cli @babel/core @babel/preset-env -D
创建.babelrc
{
"presets": ["@babel/preset-env"],
"plugins": []
}
把webpack.config.js改为webpack.dev.js
创建webpack.prod.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const srcPath = path.resolve(__dirname, './src')
const distPath = path.resolve(__dirname, './dist')
module.exports = {
mode: 'production',
entry: './src/index.js',
devServer: {
port: 5000,
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'index.html'),
filename: 'index.html',
})
],
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader'],
include: srcPath,
exclude: /node_modules/
},
]
},
output: {
filename: 'bundle.js',
path: distPath,
}
}
修改一下package.json
scripts: {
"dev": "webpack serve --config ./webpack.dev.js",
"build": "webpack --config ./webpack.prod.js"
}
运行一下pnpm run build
拆分webpack配置
先更改一下部分文件结构
mv ./index.html src/index.html
mkdir build && cd $_
touch paths.js webpack.common.js webpack.dev.js webpack.prod.js
# 安装webpack-merge和clean-webpack-plugin
pnpm i webpack-merge clean-webpack-plugin -D
paths.js
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')
const distPath = path.join(__dirname, '..', 'dist')
module.exports = {
srcPath,
distPath
}
webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { srcPath } = require('./paths')
module.exports = {
entry: path.join(srcPath, 'index'),
plugins: [
new HtmlWebpackPlugin({
template: path.join(srcPath, 'index.html'),
filename: 'index.html',
})
],
}
webpack.dev.js
const webpackCommonConf = require('./webpack.common.js')
const { merge } = require('webpack-merge')
module.exports = merge(webpackCommonConf, {
mode: 'development',
devServer: {
port: 3000,
hot: true,
},
})
webpack.prod.js
const webpackCommonConf = require('./webpack.common.js')
const { merge } = require('webpack-merge')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const { srcPath, distPath } = require('./paths')
module.exports = merge(webpackCommonConf, {
mode: 'production',
output: {
filename: 'bundle.[contenthash:8].js',
path: distPath,
},
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader'],
include: srcPath,
exclude: /node_modules/
},
]
},
plugins: [
new CleanWebpackPlugin(), // build时默认清空output.path文件夹
],
})
处理图片等静态资源(并没有等)
安装依赖
pnpm i file-loader url-loader -D
webpack.dev.js
module: {
rules: [
// 直接引入图片url
{
test: /\.(png|jpg|jpeg|gif)$/,
use: 'file-loader',
}
]
}
webpack.prod.js
module: {
rules: [
{
test: /\.(png|jpg|jpeg|gif)$/,
use: {
loader: 'url-loader',
// 小于5kb的图片用base64, 否则仍然使用file-loader
options: {
limit: 5 * 1024,
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
}
}
}
}
}
css方案
pnpm i style-loader css-loader postcss-loader autoprefixer -D
创建postcss.config.js
module.exports = {
plugins: [require('autoprefixer')]
}
修改webpack.common.js
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader'],
},
]
}
完成后可在src/index.js中这样使用
import './style.css'
准备试试css-in-js(可能能到那一步)就不装less了
处理打包后的js、css
抽离css
pnpm i mini-css-extract-plugin -D
先把common中对css的处理移到dev
然后修改webpack.prod.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
}),
],
压缩js、css
pnpm i terser-webpack-plugin optimize-css-assets-webpack-plugin -D
webpack.prod.js
const TerserJSPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
},
现在的dist文件夹结构如图所示
激动人心的分包
修改webpack.prod.js
optimization: {
splitChunks: {
// initial 入口chunk,对于异步导入的文件不处理
// async 异步chunk,只对异步导入的文件处理
// all 全部chunk
chunks: 'all',
// 缓存分组
cacheGroups: {
// 第三方模块
vendor: {
name: 'vendor',
priority: 1, // 权限更高则优先抽离
test: /node_modules/,
minSize: 0, // 大小限制
minChunks: 1, // 最少复用的次数
},
// 公共模块
common: {
name: 'common',
priority: 0,
minSize: 0,
minChunks: 2,
},
},
}
},
该成为React了
先从jsx开始
pnpm i react react-dom @babel/preset-react -D
.babelrc
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": []
}
webpack.prod.js中对js的处理移到webpack.common.js
在index.html写入<div id="root"></div>
修改index.js
import React from 'react'
import ReactDOM from 'react-dom'
ReactDOM.render(<button onClick={()=>alert('hello')}>Hello</button>, document.getElementById('root'))
完成
成为一个App
先配置一下webpack.common.js方便开发
resolve: {
extensions: ['.js', '.json'], // 可以不写扩展名的文件类型
// 很有用的别名,目前只写根目录
alias: {
'@': srcPath,
},
},
同时给vscode也配置一下对@的路径提示,在根目录新建jsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"target": "ES6",
"module": "commonjs",
"allowSyntheticDefaultImports": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
在.src/新建App.js
import React from 'react'
const App = () => {
return <div>I am an App</div>
}
export default App
现在的index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from '@/App.js'
ReactDOM.render(<App />, document.getElementById('root'))
路由(react-router)
感觉比vue-router麻烦多了,先写到这