![React 脚手架](https://tva1.sinaimg.cn/large/0081Kckwgy1gk670j0ybmj30m808w403.jpg)
前言
刚开始学习React的朋友基本都是从Facebook官方提供 create-react-app 脚手架开始,别人的脚手架开箱即用固然爽,但是有的时候想要进行一些功能的拓展(集成less等) 就需要去百度谷歌。因为脚手架把整个项目的编译构建过程高度集成了,我们想通过脚手架生成的代码学习项目工程化,刚开始肯定是大难度的。
伟人曾道:自己动手,丰衣足食。为了更深层次的去学习react,去尝试理解react项目编译构建过程,自己动手搭建最基础的一个react脚手架也许是个不错的学习方法。
「追根溯源」
首先我们必须知道,我们的工程不管多么高大上,到最后打包出来的文件无非html,css,js以及其他浏览器能够认识的文件。
React 推出自己一套新语法 JSX,正常的js文件是不允许出现 xml 标签的,但是JSX赋予了js+xml混合的能力。浏览器是不认识jsx文件的,我们为了最终能把jsx转化成js,需要借助webpack+babel工具来配合处理项目文件。对于webpack、babel详细的功能学习后面找机会单独开篇。
所以开篇的目的出发,我们需要将项目中的 jsx,tsx,less,sass,png等方便开发者操作但是浏览器不认识的文件进行编译整合,从而得到能够在浏览器或者node环境运行的文件,接下来看看到底需要怎么做。
「React 一切的开端」
-
初始化项目
// 新建一个空文件夹
mkdir levenx-init && cd levenx-init
// 生成package.json
npm init -y
-
安装webpack
npm install webpack webpack-cli webpack-dev-server -D
或者你更习惯yarn管理包
yarn add webpack webpack-cli webpack-dev-server -D
虽然webpack推崇「约定大于配置」,即不需要配置任何信息就可以打包项目。但是为了更好的管理编译过程,我们还是提供下自己的配置文件,从而让webpack更合我们的心意。
创建 webpack.config.js 配置文件
//提供一个最简单的webpack配置,并且尽可能的加上备注
const webpack = require('webpack');
const path = require('path');
const config = {
//打包模式:development || production 开发模式和生产模式
mode: 'development',
//基础阶段可以不加;配置不同的 source map 格式来增强调试过程
devtool: "cheap-module-eval-source-map",
//webpack 打包入口
entry:path.resolve(__dirname, './src/index.js'),
//webpack 打包产物输出属性
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name]-[hash].js',
},
//webpack 模块属性
module: {
},
//开发模式配置本地开发服务器
devServer: {
port: 3000,
host:'0.0.0.0',
contentBase: path.join(__dirname, 'dist'),
clientLogLevel: 'silent',
historyApiFallback: true //处理browserRouter 转发路由404问题
},
// webpack 插件配置,拓展 webpack 的超能力
plugins: [
new webpack.HotModuleReplacementPlugin()
]
};
module.exports = config;
备注
从配置文件可以很明显的看出,webpack配置文件最终需要返回一个config对象,其他的各种逻辑都可以随意的抽象。
-
安装React核心包
yarn add react react-dom
玩过React的朋友应该对**「react」「react-dom」**这两个包有很深印象。
关于这两个核心包有机会也单独开篇。
-
安装babel
开发者之所以能很愉快的使用jsx模式来编写代码,就是利用了babel提供的功能。
yarn add @babel/core @babel/plugin-transform-runtime @babel/runtime -D
yarn add @babel/preset-env @babel/preset-react -D
yarn add babel-loader -D
//处理装饰器decorators 如果你使用redux connect这类高阶组件时喜欢注解方式,需要安装
yarn add @babel/plugin-proposal-decorators -D
创建bable配置文件 .babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-transform-runtime"
],
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
]
}
备注
Babel 配置信息可以通过项目跟路径的.babelrc文件来配置,也可以直接在webpack中model.rule中配置。单独抽出配置文件会更加清晰,推荐使用.babelrc。
.babelrc 需要严格遵守json格式编辑。
额外知识点
presets与plugins的区别?
Babel是代码转换器,比如将ES6转成ES5,或者将JSX转成JS等。借助Babel,开发者可以提前用上新的JS特性,实现Babel代码转换功能的核心,就是Babel插件(plugin)。
Babel插件一般尽可能拆成小的力度,开发者可以按需引进。比如对ES6转ES5的功能,Babel官方拆成了20+个插件。这么做的好处是既提高了性能,也提高了扩展性。缺点也显而易见,但很多时候,为了拥有某种功能需要逐个插件引入,既费力,又容易出错。
此时babel提供了 presets 的概念,。简单的说 Babel Preset可以看作是Babel Plugin的集合。
webpack配置使用babel-loader
//提供一个最简单的webpack配置,并且尽可能的加上备注
const webpack = require('webpack');
const path = require('path');
const config = {
...
//webpack 模块属性
module: {
rules: [
// 使用 bable 处理jsx文件,webpack处理项目文件时,碰到jsx文件,就使用babel进行代码转化
{
test: /\.jsx?$/,
exclude: /node_moudles/,
use: [
{ loader: 'babel-loader' }
]
}
]
},
....
};
module.exports = config;
现在 关于webpack + babel 的最基础的满足react项目的配置已经完成了。
接下来 就是见证成果的时候了。
-
启动dev服务器
//方案1: 终端执行
npx webpack-dev-server --config webpack.dev.config.js --progress --colors --hot --inline --open
//方案2: npm-script模块
"scripts": {
"start": "webpack-dev-server --config webpack.dev.config.js --progress --colors --hot --inline --open"
}
npm run start
等等,停下脚步,写个最简单的 React dome,看看效果。
-
代码
![乐闻世界react代码](https://tva1.sinaimg.cn/large/0081Kckwgy1gk6bk2252uj31cp0u0n8m.jpg)
-
效果
乐闻世界效果图
可见已经可以正常的编写react代码,但是想开心的写代码还需要新增一些配置。
「我想要更多的能力」
常用webpack插件配置
// 处理html,将打包好的js路径引入到html中等等
yarn add html-webpack-plugin -D
// 清空历史打包产物
yarn add clean-webpack-plugin -D
//webpack.js中配置插件
const HtmlwebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
...
plugins: [
new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(),
new HtmlwebpackPlugin({
title: 'hello levenx',
template: './public/index.html'
})
]
样式处理
yarn add style-loader css-loader less-loader less -D
Webpack.config.js 配置loader
module: {
rules: [
{
test: /\.(le|c)ss$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{ loader: 'less-loader' }
]
},
{
test: /\.js$/,
exclude: /node_moudles/,
use: [
{ loader: 'babel-loader' }
]
}
]
},
备注
配置完样式的loader,我们就是开心的写样式了。
「独乐乐不如众乐乐」
既然都能在本地运行项目了,那么我们何不想办法把你的杰作丢到公网上,让别人都能欣赏呢。
这个时候可能有人疑惑,既然都能在本地运行了,同样的把代码丢到远程服务器上,然后npm run start 不就行了吗。虽然这种方式是可以把项目启动起来,但是是利用 webpack-dev-server 开发服务器提供的能力。开发服务器是很耗机器性能的,因为存在各种热更新,这都是通过长链接实现的。小流量来访问你的网站可能还能扛得住,但是一旦流量大了,你的网站会变得卡顿,甚至宕机。
那我们该怎么办呢?接下来提供一个简单的方案。
生产资源打包配置
// webpack.prod.config.js
const config = {
//模式设置成production,打包产物会剔除不必要的代码
mode: 'production',
entry: [
'./src/index.js'
],
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name]-[hash].js',
//如果你想利用CDN加速静态资源加载,这个地方需要配置CDN地址
publicPath: 'http://static.levenx.com/'
},
...
};
module.exports = config;
执行打包命令
// npm-script
"scripts": {
"build": "webpack --mode=production --config webpack.prod.config.js"
}
npm run build
备注
根目录下的build文件夹就是打包出来的文件。
把打包出来的所有文件丢到你的静态资源服务器上即可。Nginx部署静态资源的方法,供大家参考。https://blog.csdn.net/m0_37890289/article/details/82466239
![]()
完整webpack.config.js 代码
//完整的webpack.config.js
const webpack = require('webpack');
const path = require('path');
const config = {
//打包模式:development || production 开发模式和生产模式
mode: 'development',
//基础阶段可以不加;配置不同的 source map 格式来增强调试过程
devtool: "cheap-module-eval-source-map",
//webpack 打包入口
entry:path.resolve(__dirname, './src/index.js'),
//webpack 打包产物输出属性
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name]-[hash].js',
//如果你想利用CDN加速静态资源加载,这个地方需要配置CDN地址
publicPath: 'http://static.levenx.com/'
},
//webpack 模块属性
module: {
rules: [
// 使用 bable 处理jsx文件,webpack处理项目文件时,碰到jsx文件,就使用babel进行代码转化
{
test: /\.jsx?$/,
exclude: /node_moudles/,
use: [
{ loader: 'babel-loader' }
]
},
// less,css 文件处理
{
test: /\.(le|c)ss$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{ loader: 'less-loader' }
]
}
],
resolve: {
// 设置别名
alias: {
'@': path.resolve(__dirname, 'src')// 这样配置后 @ 可以指向 src 目录
}
},
},
//开发模式配置本地开发服务器
devServer: {
port: 3000,
host:'0.0.0.0',
contentBase: path.join(__dirname, 'dist'),
clientLogLevel: 'silent',
historyApiFallback: true //处理browserRouter 转发路由404问题
},
// webpack 插件配置,拓展 webpack 的超能力
plugins: [
new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(),
new HtmlwebpackPlugin({
title: '乐闻世界',
template: './public/index.html'
})
]
};
module.exports = config;