前言
公司目前正在启动一个新项目,之前用的架构整体来说版本都比较低,出于时间空余,周末来公司加加班,自己整理一套新的东西,目前项目还没启动,有些不足之处还在优化,现在分享出来,大家互相学习,有什么不足之处,望指出! 这次新的项目使用react全家桶,主要是webpack4 react16.8 数据状态管理用的redux-saga 数据交互这块主要用的就是axios
开始表演
打开终端 创建项目目录 mkdir my_app 生成package文件: npm init -y
webpack篇
首先我们需要安装我们的主角webpack相关依赖 && react相关依赖
yarn add react react-dom react-router-dom webpack webpack-cli webpack-dev-server
or
npm i react react-dom react-router-dom webpack webpack-cli webpack-dev-server --save-dev
配置webpack公共配置文件
在根目录新建webpack.basic.config.js 此文件主要配置 入口文件 entry 输出文件:output 以及我们的loader 和 plugins; 我们开始安装所需要用到的loader包
yarn add babel-core babel-loader babel-plugin-import babel-plugin-transform-runtime babel-preset-latest babel-preset-react babel-preset-stage-0 style-loader url-loader
less less-loader
我们来开始写入口代码
entry: {
bundle: path.resolve(__dirname, './src/main.js')
},
出口代码:
output: {
path: path.resolve(__dirname, './build'),
filename: '[name].[hash].js',
chunkFilename: '[name].chunk.[hash:5].js',
publicPath: '/'
},
配置loader
module: {
rules: [
{
test: /\.js|jsx$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.(less|css)$/,
include: path.resolve(__dirname, './src/components'),
use: [
miniCssExtractPlugin.loader, // webpack4.x
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true,
camelCase: true,
localIdentName: '[path][name]__[local]--[hash:base64:5]'
}
}, // react css module
'resolve-url-loader',
'px2rem-loader', 'postcss-loader', 'less-loader'
]
},
{
test: /\.(less|css)$/,
include: path.resolve(__dirname, './src/css'),
use: [miniCssExtractPlugin.loader, 'css-loader', 'px2rem-loader', 'postcss-loader', 'less-loader']
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: 'images/[hash].[ext]' // 所有图片在一个目录
}
}
]
}
]
},
抽离公共文件:
optimization: {
// 抽离webpack runtime到单文件
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
// 最大初始请求数量
maxInitialRequests: Infinity,
// 抽离体积大于80kb的chunk
minSize: 80 * 1024,
// 抽离被多个入口引用次数大于等于1的chunk
minChunks: 1,
cacheGroups: {
// 抽离node_modules下面的第三方库
vendor: {
test: /[\\/]node_modules[\\/]/,
// 从模块的路径地址中获得库的名称
name (module, chunks, chacheGroupKey) {
const packageName = module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)/
)[1];
return `vendor_${packageName.replace('@', '')}`;
}
}
}
}
},
安装插件yarn add mini-css-extract-plugin clean-webpack-plugin 配置插件
plugins: [
// eslint-disable-next-line new-cap
new miniCssExtractPlugin({
filename: '[name].[contentHash].css',
chunkFilename: '[id].[contentHash].css'
}),
new webpack.DefinePlugin({// 设置成production去除警告
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new CleanWebpackPlugin(['dist',
'build'], {
root: __dirname,
verbose: true,
dry: false,
exclude: ['jslibs']
})
]
webpack.basic.config.js完整代码
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const miniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: {
bundle: path.resolve(__dirname, './src/main.js')
},
output: {
path: path.resolve(__dirname, './build'),
filename: '[name].[hash].js',
chunkFilename: '[name].chunk.[hash:5].js',
publicPath: '/'
},
module: {
rules: [
{
test: /\.js|jsx$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.(less|css)$/,
include: path.resolve(__dirname, './src/components'),
use: [
miniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true,
// namedExport: true, // this is invalid Options ,I find it
camelCase: true,
localIdentName: '[path][name]__[local]--[hash:base64:5]'
}
}, // react css module
'resolve-url-loader', // may need this (https://www.npmjs.com/package/resolve-url-loader)
'px2rem-loader', 'postcss-loader', 'less-loader'
]
},
{
test: /\.(less|css)$/,
include: path.resolve(__dirname, './src/css'),
use: [miniCssExtractPlugin.loader, 'css-loader', 'px2rem-loader', 'postcss-loader', 'less-loader']
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: 'images/[hash].[ext]' // 所有图片在一个目录
}
}
]
}
]
},
performance: {
hints: false
},
optimization: {
// 抽离webpack runtime到单文件
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
// 最大初始请求数量
maxInitialRequests: Infinity,
// 抽离体积大于80kb的chunk
minSize: 80 * 1024,
// 抽离被多个入口引用次数大于等于1的chunk
minChunks: 1,
cacheGroups: {
// 抽离node_modules下面的第三方库
vendor: {
test: /[\\/]node_modules[\\/]/,
// 从模块的路径地址中获得库的名称
name (module, chunks, chacheGroupKey) {
const packageName = module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)/
)[1];
return `vendor_${packageName.replace('@', '')}`;
}
}
}
}
},
plugins: [
// eslint-disable-next-line new-cap
new miniCssExtractPlugin({
filename: '[name].[contentHash].css',
chunkFilename: '[id].[contentHash].css'
}),
new webpack.DefinePlugin({// 设置成production去除警告
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new CleanWebpackPlugin(['dist',
'build'], {
root: __dirname,
verbose: true,
dry: false,
exclude: ['jslibs']
})
]
};
以上公共配置文件已经完成,我们后续要对生产环境和开发环境进行配置 首先我们来配置开发环境。通过webpack-merge来合并公共配置
const path = require('path');
const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const baseWebpackConfig = require('./webpack.base.config');
module.exports = merge(baseWebpackConfig, {
mode: 'development',
devServer: {
host: 'localhost',
port: 3334,
contentBase: path.resolve(__dirname, './build'),
historyApiFallback: true,
compress: true
},
devtool: 'inline-source-map',
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: './index.html',
inject: 'body'
})
]
})
生产环境配置文件
const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// const TerserWebpackPlugin = require("terser-webpack-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
const baseWebpackConfig = require('./webpack.base.config');
module.exports = merge(baseWebpackConfig, {
mode: 'production',
// 对线上代码压缩
optimization: {
minimizer: [
new OptimizeCssAssetsWebpackPlugin(),
// new TerserWebpackPlugin({ sourceMap: true }),
new HtmlWebpackPlugin({
filename: 'index.html',
template: './index.html',
inject: 'body',
minify: {
collapseWhitespace: true,
removeComments: true,
removeAttributeQuotes: true
}
})
]
}
});
以上我们所有的webpack文件都配置完成了,下面我们开始搭建项目结构,集成redux-saga
搭建项目结构
目录结构
yarn add react react-css-modules react-dom react-redux react-router-dom redux redux-saga axios
main.js
import React from 'react'
// eslint-disable-next-line semi
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux'
import store from './redux/store/store';
import { app } from './app';
import '../src/css/app.less';
const render = Component => {
ReactDOM.render(
<AppContainer>
<Provider store={store}>
<Component />
</Provider>
</AppContainer>,
document.getElementById('root')
)
}
render(app)
// Webpack Hot Module Replacement API
if (module.hot) {
module.hot.accept('./app', () => {
render(app)
})
}
app.js
import React from 'react';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
import Bundle from './components/bundle';
// 按需加载
const Home = (props) => (
<Bundle load={() => import('./pages/home')}>
{(Home) => <Home {...props}/>}
</Bundle>
);
export const app = () => (
<Router>
<Switch>
<Route exact path="/home" component={Home} ></Route>
<Redirect to='home'/>
</Switch>
</Router>
)
按需加载 bundle.js
import { Component } from 'react';
export default class Bundle extends Component {
constructor (props) {
super(props);
this.state = {
mod: null
};
}
componentWillMount () {
this.load(this.props)
}
componentWillReceiveProps (nextProps) {
if (nextProps.load !== this.props.load) {
this.load(nextProps)
}
}
load (props) {
this.setState({
mod: null
});
// 注意这里,使用Promise对象; mod.default导出默认
props.load().then((mod) => {
this.setState({
mod: mod.default ? mod.default : mod
});
});
}
render () {
return this.state.mod ? this.props.children(this.state.mod) : null;
}
}
以上代码就不一一列出了,大家可以进入我的仓库直接观看,谢谢! github.com/long-joan/w…