前言
很早之前写了个codepen的react教程,方案很简单,引入react、react-dom依赖,其他主要是要熟悉codepen的使用。 这个方案对于想快速上手了解react的有一定用处,但是在实际项目中还是需要本地配置一套基于react的项目结构。
目前脚手架存在的问题
一般的框架都会有对应的cli,比如vue-cli, angualr-cli, create-react-app,最近发现react、vue的cli都将细节配置给隐藏掉了(主要是webpack配置),只能通过config变量自定义webpack配置,各种框架的方式还不太一样,react需要单独引入新的库去修改,好处是屏蔽了细节配置,将最复杂的webpack配置隐藏掉,用户开箱即用,坏处是对于初学者来说屏蔽了最复杂的配置项, 进阶的路上容易忽略掉重点。
最近有个小需求,也开始从0搭建了一个react项目, 异常的艰难,太久没有关注这些玩意了,网上可能相关的资料已经很多了,算是记录一下自己的历程吧。
正文
准备工作
一台电脑, 本地安装node、npm, vscode, chrome浏览器
涉及到的技术
react、webpack、babel、antd、less、postcss
步骤
- 首先npm init 生成package.json,生成相应的依赖
- 这里要关注一下devDep和dep的区别,devDependencies是基于本地环境的,代码不会打包到线上代码
- 创建webpack.config.js, 关注的主要几个点:
- entry: 入口文件
- output: 编译后的输入文件
- modules: ruels
- babel-loader 处理react、es678* 转成es5
- url-loader, file-loader 处理图片, 默认转成base64,超出将图片copy
- less-loader, css-loader, postcss-loader, 这里强调一下postcss, 主要是autoprefix功能, 自动区全不同浏览器的差异, 使用方式,可以在这里增加options,也可以单独创建postcss.config.js, 同事autoprefix的具体配置,最近的定义需要配置在package.json里面.
- devtool: 'source-map' 设置sourcemap
- resolve
- extensions,默认处理的后缀
- alias,自定义一些目录引用,避免在层级比较深的时候使用相对路径
- dev-server: 基于express 本地启动服务
// package.json
"scripts": {
"build": "webpack --config webpack.config.js",
"start": "webpack-dev-server --config webpack.dev.config.js"
},
"browserslist": [
"last 5 versions",
"> 1%",
"not ie <= 8"
],
- 创建代码, 注意: 我这个例子只是为了写几个公共组件,没有引入react-router,mobx或者redux方案
- index.html: 主要是id,在webpack配置中使用HtmlWebpackPlugin 将js和html关联
- index.js, react-dom引用
- 创建app.js, antd引用,具体组件引用
- 具体react代码书写
- 如果需要引用移动端适配, 目前使用的是淘宝的flexible方案, 除了在js中引入'amfe-flexible', 需要在postcss配置 pxtorem, require('postcss-pxtorem')
- 目录结构src/
- assets/*, 存放图片、字体等静态资源
- pages/* 具体页面代码
- components/* 公共组件
- App.js 主引用代码
<!-- index.html -->
<html>
<head>
<meta charset="utf-8">
<title>公共模块</title>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</head>
<body>
<div id="root" />
</body>
</html>
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.less';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// app.js
import React from 'react';
import { ConfigProvider } from 'antd';
import HeaderComponent from './components/header';
import zhCN from 'antd/es/locale/zh_CN';
import './App.less';
const App = () => (
<ConfigProvider locale={zhCN}>
<HeaderComponent />
</ConfigProvider>
);
export default App;
webpack如下: (目前只是一个可执行的方案,很多优化还没往里面加)
// webpack.congig.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');
const path = require('path');
module.exports = {
mode: 'development', // 'production' | 'development' | 'none'
entry: path.resolve(__dirname, 'src/index.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/env', '@babel/react'],
plugins: [
"@babel/plugin-proposal-class-properties"
]
}
}
},
// {
// test: /\.(png|jpg|gif)$/i,
// use: [
// {
// loader: 'url-loader',
// options: {
// limit: 8192
// },
// },
// ],
// },
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[sha512:hash:base64:7].[ext]',
},
},
],
},
{
test: /\.less$/,
exclude: /node_modules/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../',
hmr: process.env.NODE_ENV === 'development',
},
},
{
loader: 'css-loader'
}, {
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
require('autoprefixer')()
]
}
}, {
loader: 'less-loader'
}
]
}
]
},
plugins: [
new webpack.ProgressPlugin(),
new HtmlWebpackPlugin({ template: './index.html' }),
new MiniCssExtractPlugin({
filename: "css/[name].css",////都提到build目录下的css目录中
chunkFilename: "[id].css"
})
],
devtool: 'source-map',
resolve: {
extensions: ['.js', '.json', '.jsx', '.css'],
alias: {
'@': path.resolve(__dirname, 'src')
}
},
devServer: {
proxy: { // proxy URLs to backend development server
'/api': 'http://localhost:3000'
},
contentBase: path.join(__dirname, ''), // boolean | string | array, static file location
compress: true, // enable gzip compression
historyApiFallback: true, // true for index.html upon 404, object for multiple paths
hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
https: false, // true for self-signed, object for cert authority
noInfo: false, // only errors & warns on hot reload
port: 8080
// ...
},
}
package.json
{
"name": "react-demo",
"version": "0.1.0",
"private": true,
"dependencies": {
"amfe-flexible": "^2.2.1",
"antd": "^3.23.1",
"babel-plugin-import": "^1.12.1",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-scripts": "^2.1.8"
},
"scripts": {
"build": "webpack --config webpack.config.js",
"start": "webpack-dev-server --config webpack.dev.config.js"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"devDependencies": {
"@babel/core": "^7.6.2",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/preset-env": "^7.6.2",
"autoprefixer": "^9.6.1",
"babel-loader": "^8.0.6",
"babel-preset-react": "^6.24.1",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.2.0",
"file-loader": "^4.2.0",
"html-webpack-plugin": "^3.2.0",
"less": "^3.10.3",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.8.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"postcss-pxtorem": "^4.0.1",
"style-loader": "^1.0.0",
"sugarss": "^2.0.0",
"terser-webpack-plugin": "^2.1.2",
"url-loader": "^2.1.0",
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9",
"webpack-dev-server": "^3.8.1"
}
}
如果要完整的掌握react,仅仅了解它本身是不够的,还需要了解webpack、babel、redux等。
就酱!!!