1 开发环境准备(windows)
1.1 安装 nodejs 和 npm
1) 下载 nodejs 安装包:http://nodejs.org/en/download/
nodejs 安装时会同时安装 npm
2) 安装完成后检查是否安装成功
命令行输入以下命令,查看 npm 和 node 版本:npm -vnode -v
若未安装成功可检查环境变量是否安装时自动设置成功
1.2 安装 create-react-app(react 官方提供的脚手架)
命令行输入:npm install -g create-react-app
create-react-app:可以用来快速创建 react 项目 -g:全局安装 create-react-app 脚手架工具,这个步骤只需要执行一次
1.3 安装 express-generator(express 官方提供脚手架)
命令行输入:npm install express-generator -g
express-generator:可以用来快速创建 express 应用 -g:全局安装 express-generator 脚手架工具,这个步骤只需要执行一次
2 创建 react+express 项目
前端框架:react 服务端:基于 node 的 express 框架
两者结合快速创建 web 项目。由于服务端代码需要部署到服务器,为了方便操作,先创建 react 项目,然后在 react 项目目录下创建 express 项目,将 react 的打包目录设置为 express 项目下的 public 文件。
(1) 创建 react 项目(client)
create-react-app myapp
(2) 创建 express 项目(server)
cd myapp
express-generator –view=ejs server
添加模版引擎:–view=ejs,此处选择 ejs 作为模版引擎。还可以选择 pub、jade 等其它模版引擎
2.2 react 初始项目目录结构
使用 create-react-app 创建的项目,已经把 webpack、babel 等配置都封装到依赖项目 react-script 中,因此在目录外层无法看到 webpack 等配置文件。
1)自动生成的项目目录介绍
A. node_modules:项目依赖包目录,使用 npm install xxx 相关命令安装的依赖都会自动下载到该目录,无需提交至 git;
B. public:公共目录,该目录下的文件都不会被 webpack 进行加载、解析和打包;通过 npm run build 进行打包时该目录下的所有文件将会直接被复制到 build 目录下;
a) favicon.ico:网站图标(可替换删除)
b) index.html:页面模板,webpack 打包后将输出文件引入到该模板内;index.html 中通过环境变量 %PUBLIC_URL% 指向 public 目录路径;
c) manifest.json:PWA 将应用添加至桌面的功能的实现依赖于 manifest.json。通过 manifest.json 文件可以对
C. src: 是源码目录,该目录下除了 index.js App.test.js registerServiceWorker.js 文件具有一定意义其余文件都是演示使用可直接删除。
a) index.js: 是整个项目的入口文件,也是 webpack 打包的入口文件;
b) App.js:项目创建后,可通过修改此文件来修改页面内容
c) App.test.js: 测试单元演示文件,暂时并不知道干嘛用;可以直接删除;
d) registerServiceWorker.js: service worker 是在后台运行的一个线程,可以用来处理离线缓存、消息推送、后台自动更新等任务;registerServiceWorker 就是为 react 项目注册了一个 service worker,用来做资源的缓存,这样你下次访问时,就可以更快的获取资源。而且因为资源被缓存,所以即使在离线的情况下也可以访问应用(此时使用的资源是之前缓存的资源)。注意,registerServiceWorker 注册的 service worker 只在生产环境中生效,并且该功能只有在 https 下才能有效果;
D. .gitignore: 该文件是 github 过滤文件配置,即指定无需提交至 git 而忽略的文件,帮助查看方式,git help ignore
E. README.md: 该文件是描述文件
F. package.json: 定义了项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。部分依赖模块被隐藏;
G. package.lock.json: 每次通过 npm 添加依赖或者更新包版本时 npm 都会把相关版本信息写入 package.lock.json 文件;
2)package.json 配置
3)可用脚本命令说明
首先说明:通过 npm run 执行下面命令实际上是运行 node_modules/react-srcipt/script 下对应的脚本文件;
A.npm run start : 开始项目,通过 http://localhost:3000 可访问项目;
B. npm run build : 项目打包,在生产环境中编译代码,并放在 build 目录中;所有代码将被正确打包,并进行优化、压缩同时使用 hash 重命名文件;执行该命令前需要在 package.json 中新增条配置 ”homepage”: “.”(上面配置文件已给出), 同时 build 后的项目需要在服务器下才能访问;否则打开的将是空白页面;
C. npm run test : 交互监视模式下启动测试运行程序;
D. npm run eject : 将隐藏的配置导出;需要知道的是 create-react-app 脚手架本质上是使用 react-scripts 进行配置项目,所有配置文件信息都被隐藏起来 (node_modules/react-scripts);当需要手动修改扩展 webpack 配置时有时就需要将隐藏的配置暴露出来;特别需要注意的是该操作是一个单向操作,一旦使用 eject,那么就不能恢复了 (再次将配置隐藏);
2.3 react 项目配置
create-react-app 默认生成的是单入口单出口生产环境,统一通过 react-script 进行管理,无法满足复杂的多入口项目的需要,因此需要对项目进行配置,使其满足实际项目需要。可通过 npm run eject 来暴露所有内建配置,以方便我们对项目的配置进行修改。
2.3.1 npm run eject
进入 myapp 根目录,执行以下命令:npm run eject。暴露所有内建配置,项目下会新增或对部分配置文件进行修改。根目录下新增 config(配置文件)和 script(脚本文件)目录。
注意:此操作不可逆,一旦执行无法回退;修改配置的其它方法:也可考虑采用 react-app-rewired 插件来实现配置覆盖。
2.3.2 多入口配置
项目默认只有 index.js(src 目录下)这一个入口文件。
以在 src 目录下新增入口文件 admin.js 为例。需修改 config 中的配置文件来:
1)修改 webpcak.config.dev.js 文件
A. 修改 entry(新增 js 文件入口配置)
// 这里我已经写成对象格式了
// 有多少个页面就添加多少个 key:value
// 这里我已经添加了一个 admin
// 数组中的 paths.appSrc+’/admin.js’ 就是这个 html 页面的入口文件
entry: {
index:[
require.resolve(‘./polyfills’),
require.resolve(‘react-dev-utils/webpackHotDevClient’),
paths.appIndexJs,
],
admin:[
require.resolve(‘./polyfills’),
require.resolve(‘react-dev-utils/webpackHotDevClient’),
paths.appSrc + ‘/admin.js’,
]
}
B. 修改 plugins 中的 HtmlWebpackPlugin(配置 html 模版文件)
// 多少个页面就 new 多少个 HtmlWebpackPlugin
// 并且在每一个里面的 chunks 都需要和上面的 entry 中的 key 匹配
// 例如上面 entry 中有 index 和 admin 这两个。
// 这里的 chunks 也需要是 index 和 admin
new HtmlWebpackPlugin({
inject: true,
chunks:[“index”],
template: paths.appHtml,
}),
new HtmlWebpackPlugin({
inject: true,
chunks:[“admin”],
template:paths.appHtml,
filename:’admin.html’
}),
C. 修改 output
// 由于原配置入口文件只有一个,因此 output 中的 filename 是写死的,
// 增加多入口之后,输出文件名被写死,对应生成了多个 boundle.js,
// 后面生成的会覆盖前面生成的文件,所以需要制定输出的文件名不能写死
output: {
path:paths.appBuild,
pathinfo: true,
filename: ‘static/js/[name].bundle.js’,
chunkFilename: ‘static/js/[name].chunk.js’,
publicPath: publicPath,
devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, ‘/’),
},
2)修改 config 下 webpack.config.prod.js 文件
A 修改 entry
// 这里的 paths.appIndexJs 和 paths.appSrc+’/admin.js’ 是入口文件
entry:{
index:[
require.resolve(‘./polyfills’),
paths.appIndexJs
],
admin:[
require.resolve(‘./polyfills’),
paths.appSrc+’/admin.js’
]
}
B 修改 plugins 中的 HtmlWebpackPlugin
// 和开发环境下一样,多少个 html 就 new 多少个 HtmllWebpackPlugin,每个都需要指定 chunks, 并且指定 filename,在 minify 中配置是否压缩 js、css 等,这是生产环境下的配置
new HtmlWebpackPlugin({
inject: true,
chunks:[“index”],
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
new HtmlWebpackPlugin({
inject: true,
chunks:[“admin”],
template: paths.appHtml,
filename:’admin.html’,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
3)在开发环境中如果想通过地址访问不同页面,需要修改 webpackDevServer.config.js
A 修改 historyApiFallback
// 这里的 rewrites:[{from: /^\/admin.html/, to: ‘/build/admin.html’}] 数组里面是一个个对象,
// 对象中前面的值是在开发时候访问的路径,例如 npm run start 之后会监听 localhost:3000,
// 此时在后面加上 /admin.html 就会访问 admin.html 中的内容,默认是访问 index.html;
// 数组中的第二个值是生产环境下的文件的路径。
// 如果有很多页面,就在 rewrites 中添加更多对象
historyApiFallback: {
disableDotRule: true,
rewrites: [
{from: /^\/admin.html/, to: ‘/build/admin.html’},
]
},
2.3.3 前端跨域问题配置
生产环境:本文中的项目,由于打包后的代码会放在 server 目录下的 public 文件夹下,也就是打包后的代码和 server 在同域下,不存在跨域问题。开发环境:开发时,前端 react 项目和后端 express 项目运行时端口端口不同,存在跨域问题。
开发环境跨域问题解决办法:在 package.json 中加入:”proxy”:http://localhost:5000 // 后端所在域。如果需要后端存在多个域:
//package.json 中加入
“proxy”: {
“/api1”: {
“target”: “http://api1.xxxx.com”,
“changeOrigin”:true
},
“/api2”:{
“target”:”http://api2.xxxx.com”,
“changeOrigin”:true
}
}
2.3.4 文件路径简化配置
当页面嵌套过深时,import Apis from ‘../../common/apis’,可通过 webpack 配置来简化路径。
// 修改 webpack.config.dev 与 webpack.config.prod 两个文件,加入相同配置
// 增加方法
function resolve(dir) {
return path.join(__dirname, ‘..’, dir)
}
// 修改 alias 配置
alias: {
‘react-native’: ‘react-native-web’,
// 加入配置
‘@src’: resolve(‘src’)
}
添加上述配置后,引入文件方式:import Apis from ‘@src/common/apis’
缺点:此方法能简化引用方法,但无法通过快捷键进入该引用文件。
2.3.5 webpack 打包体积详情分布
1)安装:npm install –save-dev webpack-bundle-analyzer
2)配置:
// 修改 webpack.prod.conf.js 文件,增加如下内容
const BundleAnalyzerPlugin = require(‘webpack-bundle-analyzer’).BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
// 修改 package.json 文件,在 scripts 中增加如下命令
“analyz”:“NODE_ENV=production npm_config_report=true npm run build”
3)运行 npm run build 或 npm run analyz,浏览器会自动打开 127.0.0.1:8888,如下页面,可查看打包后文件分布,以及打包文件大小。
2.3.6 webpack 构建打包优化
1)使用 UglifyJSPlugin 压缩 js 文件
安装方法: npm install uglifyjs-webpack-plugin –save-dev
在 webpack.config.prod.js 文件中添加
const UglifyJsPlugin = require(‘uglifyjs-webpack-plugin’)
module.exports = {
plugins: [
new UglifyJSPlugin(),
]
}
2)生产环境去掉 map 文件:缩短 build 时间
// 修改 webpack.config.prod.js 文件:
// 注释 devtool:shouldUseSourceMap? ‘source-map’:false
devtool:false,// 增加
3)添加 cache-loader,减少打包时间
// 修改 webpack.config.dev.js 文件:
module:{
rules:[{
use:[
// 添加在最前面
‘cache-loader’,
]
}]
}
在其它加载程序加载之前添加以将结果缓存在磁盘上
4)提取公共包:提取多个入口引入的公共依赖包
修改 webpack.config.prod.js 文件
// 修改 entry 文件,
entry:
// 这里的 paths.appIndexJs 和 paths.appSrc+’/admin.js’ 依然是每个 html 的入口文件
{
index:[
require.resolve(‘./polyfills’),
paths.appIndexJs,
],
admin:[
require.resolve(‘./polyfills’),
paths.appSrc+’/admin.js’
],
// 增加 vendor
vendor:[‘react’,’react-dom’]
},
// 修改 plugin
plugin:{
// 新增以下代码
new webpack.optimize.CommonsChunkPlugin({
name: [“vendor”],
// filename:’static/js/vendor.[chunkhash:8].js’,
// minChunks: 3 // 三方库在逻辑代码中被调用两次 (数字可以自定义),将公共的代码提取出来
}),
/* 防止 vendor hash 变化 */
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: ‘manifest’,
chunks: [‘vendor’]
}),
}
// 修改 plugins 中的 HtmlWebpackPlugin,在 chunks 中添加需要引入的公共包,
// 其中公共包需放在后面,使其在加入 html 页面时能在其它 js 文件前面
new HtmlWebpackPlugin({inject: true, chunks:[“index”,”vendor”], template: paths.appHtml, minify: {removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true,},}),new HtmlWebpackPlugin({inject: true, chunks:[“admin”,”vendor”], template: paths.appHtml, filename:’admin.html’, minify: {removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true,},}),
2.3.7 build 命令
命令行输入:npm run build,出现以下文件夹,其中 admin.html 和 index.html 分别是不同的入口。
2.3.8 修改 build 后资源文件根路径
可在 path.js 文件中修改打包后资源文件路径,例如修改 path.js 文件中 getServedPath 方法中的‘/’,改为‘/build’,则打包后资源文件的路径会加上 build,修改前资源文件路径中是没有 build 的。
本文中 react+express 的项目,无需修改资源文件根路径,因为 express 会配置资源文件所在目录为。
2.4 express 项目配置(与 react 结合)
2.4.1 nodemon 热启动
express 框架中启动项目后,文件更新后需要手动重启 node 服务,修改才会生效。使用 nodemon 可以实现热启动,文件修改后自动重启使修改生效。在 server 根目录(express 项目根目录)下运行以下命令,安装 nodemon:npm –save-dev install nodemon
// 修改 server(express 项目根目录)目录下的 package.json 文件,将 node 改为 nodemon
// 将 ”start”:”node ./bin/www” 改为:
“start”:”nodemon ./bin/www”
2.4.2 react 打包目录相关修改
为了方便,将 react 打包目录修改为 server 目录下 public 目录,可以避免每次打包后都需要将 build 目录下的文件在复制到 server 目录下。
// 修改 path.js 文件,module.exports 中的 appBuild 变量
// 将 appBuild: resolveApp(‘build’), 改为
appBuild: resolveApp(‘server/public’),
2.4.3 在 react 的 package.json 中增加 server 的启动命令
在 webstorm 中,会自动出现根目录下 package.json 中的 scripts 下的 npm 命令,为了方便启动 server,可在 react 根目录下的 package.json 文件中增加 server 的启动项。
“scripts”: {
“start”: “node scripts/start.js”,
“server-start”: “cd server && npm run start”,// 增加 server 启动命令
“build”: “node scripts/build.js”,
“test”: “node scripts/test.js”
},
2.5 react 开发调试工具
2.5.1 react-developer-tools
浏览器扩展工具中搜索此插件并安装,可以查看到 react 组件结构
2.5.2 chrome 下的 source map
chrome 引入了 source-map 文件,可以查看打包前代码。唯一要做的就是配置 webpack 自动生成 source-map 文件,这也很简单,在 webpack.config.js 中增加一行配置即可(需要重新启动 webpack-dev-server 使配置生效),create-react-app 已做此配置,因此不需要再修改。
2.5.3 Eslint(javascript 代码检查工具)
Create-react-app 已安装 Eslint,可对 eslint 进行自定义配置规则。
2.6 项目目录结构优化
2.6.1 react 项目目录结构优化
开发目录主要是 src 目录,因此需要修改的目录主要是 src 目录。
|——src
|————|common // 公共组件目录,如 http.js、cookie.js 等
|————|components // 基础组件、业务组件、业务代码抽象出的一些基础类,例如每个页面可以在此目录下建立一个文件存放相关组件。
|————|layouts // 布局相关组件及其样式文件,如 header.js、footer.js、menu.js 等
|————|styles // 公共样式文件
|————|static // 公共的静态资源文件,如公共的图片资源文件等
|————|views // 页面入口文件,可与 comonents 中的页面组件对应
如果使用了 router 和 redux 可在 src 下增加目录:
redux:redux 应用数据状态管理文件,包括 actions、reducers、stores 三个子目录
routes:路由管理模块
containers:应用根容器,用于连接 redux 和 router
2.6.2 express 项目目录结构优化
|——server // express 项目根目录
|————|bin
|——————|www // 服务器相关配置文件
|————|controllers // 控制器层,处理前端请求
|————|models // 数据库操作相关文件
|————|node_modules //npm 包安装目录
|————|public //react 打包目录,存放所有的 html,js/css/ 图片等资源文件
|————|routes // 路由文件目录
|——————|api.js //api 请求路由文件
|——————|pages.js // 页面请求路由文件
|————|utils // 公共文件目录
|——————|config.js // 各种常量或公共方法
|——————|db.js // 数据库访问方法封装
|——————|http.js //http 请求方法封装
|————|views // express 框架自带,由于打包后的文件全放在 public 目录下,因此这个文件可不用了
|————|app.js // 入口文件
|————|package.json
|————|package-lock.json