搭建目录结构
-
创建文件夹并进入mkdir react-family && cd react-family
-
init npmnpm init 按照提示填写项目基本信息,一路回车就好了,这时候文件目录下只有一个package.json文件
-
创建整体框架mkdir src && cd srcmkdir components pages layouts redux config utils services在跟目录下创建 webpack.base.config.js在pages下创建 template.html在src下创建 index.js
安装基础依赖
1.安装webpack
yarn add webpack webpack-cli --dev 或 npm install --save-dev webpack webpack-cli复制代码
yarn add html-webpack-plugin --dev复制代码
yarn add clean-webpack-plugin --dev 复制代码
使用 CleanWebpackPlugin 插件可以在每次构建前都清楚 dist 文件夹, 这样每次构建完, dist 目录下只有会生成要用到的文件
yarn add uglifyjs-webpack-plugin --dev 复制代码
用于压缩js文件,并且可以在打包的时候,去除console等
yarn add open-browser-webpack-plugin --dev 复制代码
用于开发环境执行yarn run dev 时,自动打开浏览器
2.安装babel(用于编译react,jsx,es6,js等)
yarn add @babel/cli @babel/core @babel/preset-env @babel/preset-react babel-loader --dev 或 npm i -D @babel/cli @babel/core @babel/preset-env @babel/preset-react babel-loader复制代码
这里使用的是@babel,是最新版的babel,老版的不用@符号 @babel/polyfill看情况安装
3.安装react相关的
yarn add react react-dom 或 npm i --save react react-dom (react的基础包) yarn add react-router-dom 或 npm i -S react-router-dom (react路由)复制代码
4.之前我们只是安装了针对js文件的编译,可能还会有其他的文件,如jpg等
yarn add html-loader style-loader css-loader less-loader url-loader file-loader less --dev 或 npm i -D html-loader style-loader css-loader less-loader url-loader file-loader less复制代码
其中style-loader css-loader less-loader是针对样式的,包括less,当然如果使用sass的话,可能需要安装
5.redux(可选性安装,如不使用也可以不安装,也可以在后面的步骤在来安装)
yarn add redux react-redux 或 npm i --save redux react-redux 复制代码
其实redux不知是用于react,也可以用于其他框架的前端项目,redux就是一个状态管理器,应用的状态统一管理,而react-redux更适用于react项目而已,核心其实还是redux,提供了<Provider>组件,传入store,只要子组件用connect调用之后,就能直接获取所有状态
webpack使用
-
入口(entry)
-
输出(output)
-
loader
-
插件(plugins)
一.基础配置
const path = require('path');
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
// mode: 'production', 可以在package.json里指定mode,不同的mode会启用不同的插件,如果是production,打包出来的文件会小很多
/*入口*/
entry: [
// 'react-hot-loader/patch',// 开启react代码的模块热替换(HMR)
path.join(__dirname, 'src/index.js')
],
/*输出到dist文件夹,输出文件名字为bundle.js*/
output: {
path: path.join(__dirname, './dist'),
// filename: 'bundle.js', // 该文件是在dist/index.html里写死的
// chunkFilename: '[name].js'
filename: '[name].[hash].js', // 不用上面的是因为缓存问题,如果某天我们修改了代码,可能用户还是访问的缓存的文件,因为最近开始是写死的,所以还需要用上HtmlWebpackPlugin,这个插件,每次会自动把js插入到你的模板index.html里面去。
chunkFilename: '[name].[chunkhash].js'
},
// 设置本地开发时的dev服务器
devServer: {
contentBase: path.join(__dirname, "dist"),
compress: true, // 服务器端开启压缩
port: 9000,
historyApiFallback: true,
host: '0.0.0.0',
// hot: true // 是否启用webpack-dev-server的热加载
},
module: {
rules: [{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react',
],
}
},
}]
},
plugins: [
// new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(), // 清除dist目录下以前的文件
new HtmlWebpackPlugin({
filename: 'index.html',
title: 'test',
template: path.join(__dirname, 'src/pages/template.html') // 之前的创建的模板html
}),
],
resolve: {
alias: {
"@": path.join(__dirname, 'src'),
}
},
};复制代码
"scripts": { "start": "webpack --config webpack.base.config.js --color --progress", // 加上--mode production可以设置mode "test": "echo \"Error: no test specified\" && exit 1"},复制代码
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
复制代码
import React from 'react';
import { render } from 'react-dom';
function App() {
return <div>Hello React</div>
}
render(<App/>, document.getElementById('app'));复制代码
二.webpack的一些升级应用
yarn add webpack-merge --dev 或 npm install --save-dev webpack-merge复制代码
const merge = require('webpack-merge');
const webpack = require('webpack');
const baseConfig = require('./webpack.base.config');
const path = require('path');
const OpenBrowserPlugin = require('open-browser-webpack-plugin')
module.exports = merge(baseConfig, {
// 用于调试, inline-source-map模式效率比较高, 所以在dev模式下推荐使用这个
devtool: 'inline-source-map',
mode: 'development',
// 设置dev服务器
devServer: {
contentBase: path.join(__dirname, "dist"),
compress: true, // 服务器端开启压缩
port: 9000,
historyApiFallback: true,
host: '0.0.0.0',
// hot: true // 是否启用webpack-dev-server的热加载
},
plugins: [
// 在js中注入全局变量process.env用来区分环境
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('development'),
}
}),
// 执行 yarn run dev时打开浏览器
new OpenBrowserPlugin({ url: 'http://localhost:9000' }),
],
})复制代码
const merge = require('webpack-merge');
const webpack = require('webpack');
const baseConfig = require('./webpack.base.config');
const path = require('path');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = merge(baseConfig, {
mode: 'production',
// 设置dev服务器
devServer: {
// 设置端口号,默认8080
port: 9000,
},
plugins: [
// 在js中注入全局变量process.env用来区分环境
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
}
}),
],
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
warnings: false,
parse: {},
compress: {
drop_console: true, // 打包的时候不需要console
properties: false
},
mangle: true, // Note `mangle.properties` is `false` by default.
// output: null,
// toplevel: false,
// nameCache: null,
// ie8: false,
// keep_fnames: false,
},
}),
],
},
})复制代码
"scripts": {
"start": "webpack --config webpack.prod.config.js --color --progress",
"build": "webpack --config webpack.prod.config.js --color --progress",
"dev": "webpack --config webpack.dev.config.js --color --progress",
"test": "echo \"Error: no test specified\" && exit 1"
},复制代码
-
入口起点:使用 entry 配置手动地分离代码。
-
防止重复:使用 CommonsChunkPlugin 或则 SplitChunksPlugin去重和分离 chunk( CommonsChunkPlugin已经不提倡使用)。
-
动态导入:通过模块的内联函数调用来分离代码。
entry: {index: './src/index.js',another: './src/another-module.js'},复制代码
optimization: {
splitChunks: {
cacheGroups: {
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
//打包重复出现的代码
vendor: {
name: 'vendor',
test: /node_modules\//,
// test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom|redux|react-redux)[\\/]/,
filename: '[name].bundle.js', // [name].[hash].js
chunks: "all", // 必须三选一: "initial" | "all" | "async"(默认就是异步)
minSize: 0, // 最小尺寸,默认0
minChunks: 1, // 最小 chunk ,默认1
maxInitialRequests: 5, // 最大初始化请求书,默认1
maxAsyncRequests: 1, // 最大异步请求数, 默认1
// reuseExistingChunk: true, // 可设置是否重用该chunk(查看源码没有发现默认值)
},
//打包第三方类库
commons: {
name: "commons",
// test: /common\/|components\//,
chunks: "initial",
minChunks: 2
}
}
}
},复制代码
-
Add react-hot-loader/babel to your .babelrc:
-
Mark your root component as hot-exported:
-
Make sure react-hot-loader is required before react and react-dom:
-
or import 'react-hot-loader' in your main file (before React)
-
or prepend your webpack entry point with react-hot-loader/patch
-
If you need hooks support, use React--Dom
const path = require('path');
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
// mode: 'production', 可以在package.json里指定mode,不同的mode会启用不同的插件,如果是production,打包出来的文件会小很多
/*入口*/
entry: [
// '@babel/polyfill', // 添加一些方法,比如静态方法如Array.from或Object..,实例方法如Array.prototype.include,以及generator函数
// 'react-hot-loader/patch',// 开启react代码的模块热替换(HMR)
path.join(__dirname, 'src/index.js')
],
/*输出到dist文件夹,输出文件名字为bundle.js*/
output: {
path: path.join(__dirname, './dist'),
// filename: 'bundle.js', // 该文件是在dist/index.html里写死的
// chunkFilename: '[name].js'
filename: '[name].[hash].js', // 不用上面的是因为缓存问题,如果某天我们修改了代码,可能用户还是访问的缓存的文件,因为最近开始是写死的,所以还需要用上HtmlWebpackPlugin,这个插件,每次会自动把js插入到你的模板index.html里面去。
chunkFilename: '[name].[chunkhash].js'
},
// 设置本地开发时的dev服务器
devServer: {
contentBase: path.join(__dirname, "dist"),
compress: true, // 服务器端开启压缩
port: 9000,
historyApiFallback: true,
host: '0.0.0.0',
// hot: true // 是否启用webpack-dev-server的热加载
},
module: {
rules: [{
test: /\.js$/,
// include: path.join(__dirname, 'src'),
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react',
],
}
},
}, { // options limit 8192意思是,小于等于8K的图片会被转成base64编码,直接插入HTML中,减少HTTP请求。
test: /\.(png|jpg|gif)$/,
use: [{
loader: 'url-loader',
options: {
limit: 8192
}
}]
}, {
test: /\.html$/,
loader: 'html-loader'
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: `file-loader`
}, {
// test: /\.less$/,
test: /\.(css|less)$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader", // https://github.com/webpack-contrib/css-loader
options: {
sourceMap: true,
importLoaders: 1, // Number of loaders applied before CSS loader
modules: true // 使文件能被引入
} // translates CSS into CommonJS
}, {
loader: "less-loader",
options: {
// strictMath: true,
// noIeCompat: true,
name: '[name].[hash].[ext]',
sourceMap: true, // 这个设置为true之后,在浏览器的请求里,能看到css的文件
modules: true // 使文件能被引入,放在这里没有用
} // compiles Less to CSS
}]
}]
},
plugins: [
// new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(), // 清除dist目录下以前的文件
new HtmlWebpackPlugin({
filename: 'index.html',
title: 'test',
template: path.join(__dirname, 'src/pages/template.html') // 之前的创建的模板html
}),
new webpack.optimize.RuntimeChunkPlugin({ // 用于缓存,提取模板
name: "manifest"
}),
],
optimization: {
splitChunks: {
cacheGroups: {
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
//打包重复出现的代码
vendor: {
name: 'vendor',
test: /node_modules\//,
// test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom|redux|react-redux)[\\/]/,
filename: '[name].bundle.js', // [name].[hash].js
chunks: "all", // 必须三选一: "initial" | "all" | "async"(默认就是异步)
minSize: 0, // 最小尺寸,默认0
minChunks: 1, // 最小 chunk ,默认1
maxInitialRequests: 5, // 最大初始化请求书,默认1
maxAsyncRequests: 1, // 最大异步请求数, 默认1
// reuseExistingChunk: true, // 可设置是否重用该chunk(查看源码没有发现默认值)
},
//打包第三方类库
commons: {
name: "commons",
// test: /common\/|components\//,
chunks: "initial",
minChunks: 2
}
}
}
},
resolve: {
alias: {
"@": path.join(__dirname, 'src'),
}
},
};复制代码
未完待续...关于热更新,懒加载等还未真正落实到代码中,还有babel的配置可以单独提一个文件出来,不一定都要放在webpack配置文件里