前言
很早之前写了个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等。
就酱!!!