1、前言
之前上线都是用公司的发布平台,点点按钮就完成了,就在想要是出去接私活怎么完成部署,刚好最近闲来无事,就利用阿里云折腾看看了
ps:本文代码可在github.com/MirrorHuang…查看
2、方案调研
2.1、开发构建
当然是选择react16.x + webpack4.x,这部分就不多说了
2.2、阿里云产品
阿里云的产品实在太多了,眼花缭乱。。。。
最开始我是将目光放在了对象存储 OSS这个产品,看文档它也是支持静态资源托管的,但是需要绑定已备案域名,遂放弃。。。
然后就选择了云服务器 ECS,可以根据自身需求和经济情况选择对应的产品
3、本地开发构建
3.1、Git仓库
这步就不多说了
3.2、项目目录
以MAP应用为例
-src
-components // 公共组件
-page // 页面代码
-pageA // A 页面
-components // 页面内组件
-index.jsx
-pageB // B 页面
-utils
-index.html // webpack构建时html模板
-package.json
-webpack.config.base.js // webpack 基本配置
-webpack.config.dev.js // webpack dev环境配置
-webpack.config.prod.js // webpack prod环境配置
3.3、基本依赖
package.json
主要依赖
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1",
},
"devDependencies": {
"webpack": "^4.39.1",
"webpack-dev-server": "^3.10.3"
"@babel/core": "^7.9.0",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.9.0",
"@babel/preset-env": "^7.9.0",
"@babel/preset-react": "^7.9.4",
"babel-core": "^6.26.3",
"babel-loader": "^8.1.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.7.0",
"css-loader": "^3.4.2",
"ejs-webpack-loader": "^2.2.2",
"html-webpack-plugin": "^4.0.1",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.9.0",
"react-dev-utils": "^10.2.1",
"style-loader": "^1.1.3",
"webpack-bundle-analyzer": "^3.6.1",
"webpack-cli": "^3.3.6",
"webpack-merge": "^4.2.2"
}
3.3、webpack配置
3.3.1、基本配置
主要是在webpack.config.base.js
文件
多入口以及生成各入口对应的html
const fs = require('fs');
//用于生成html
const HtmlWebpackPlugin = require('html-webpack-plugin');
const entry = {}
const htmlList = []
const files = fs.readdirSync(path.join(__dirname, './src/page'));
files.forEach(function (filename) {
entry[filename] = path.join(__dirname, './src/page', filename);
htmlList.push(new HtmlWebpackPlugin({
template: 'index.html',
filename: `${filename}/${filename}.html`,
chunks: ["runtime", "framework", /common(.*)\.js/, filename],
}))
})
module.exports = {
entry,
plugins:[...htmlList]
}
出口
module.exports = {
output: {
publicPath: '../', // 按需加载时指定目录,相对于html文件
path: path.resolve(__dirname, './dist'),
filename: process.env.NODE_ENV === 'production' ? '[name]/[name].bundle.[contenthash].js' : '[name]/[name].bundle.js',
}
}
最终打包后dist目录如下
-pageA
-pageA.html
-pageA.bundle.js
-pageB
-pageB.html
-pageB.bundle.js
值得注意是publicPath,该值会为资源添加一个前缀
loader
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
module: {
rules: [
{
test: /\.js|\.jsx$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
useBuiltIns: 'usage'
}],
'@babel/preset-react'
],
plugins: ['@babel/transform-runtime', '@babel/plugin-proposal-class-properties']
}
}
},
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader'
}, {
loader: 'less-loader'
}],
}
]
}
}
optimization
主要是将框架和第三方依赖单独抽离出来,减少包体积大小
module.exports = {
optimization: {
splitChunks: {
maxAsyncRequests: 5,
chunks: "all",
maxInitialRequests: 3,
cacheGroups: {
framework: { // 抽离react相关的框架
test: (module) => {
return /react|redux|prop-types/.test(module.context);
},
name: "framework", // 打包后的文件名,任意命名
reuseExistingChunk: true,
priority: 10, // 设置优先级,防止和自定义的公共代码提取时被覆盖,不进行打包
},
common: { // 第三方依赖
test: /node_modules\/(.*)\.js/,
name: "common",
priority: 1,
// maxSize: 3000,
}
},
},
runtimeChunk: { // 抽离webpack runtime代码,可以利用react-dev-utils/InlineChunkHtmlPlugin将其放在html script标签内,减少网络请求
name: 'runtime'
}
}
}
其余配置
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
//清除输出目录,免得每次手动删除
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
resolve: {
extensions: ['.js', '.jsx'],
},
plugins: [
new CleanWebpackPlugin(['dist']),
new InlineChunkHtmlPlugin(HtmlWebpackPlugin,[/^runtime/]),
new MiniCssExtractPlugin({
filename: process.env.NODE_ENV === 'production' ? '[name]/[name].[contenthash].css' : '[name]/[name].css'
}),
].filter(Boolean)
}
3.3.2、热加载
主要是在webpack.config.dev.js
文件
const path = require("path");
const webpack = require("webpack");
const merge = require('webpack-merge')
const baseConfig = require('./webpack.config.base')
module.exports = merge(baseConfig, {
// 热加载
devServer: {
contentBase: path.resolve(__dirname, './dist'),
hot: true,
port: 1024,
open: true,
// 必须设置,不然无法找到文件
publicPath: '/',
// 自动打开webpack-dev-server页面,可以根据构建后的目录结构设置成其他页面
openPage:'webpack-dev-server'
},
devtool: 'cheap-module-source-map',
mode: 'development',
plugins: [
// 热加载
new webpack.HotModuleReplacementPlugin(),
// development 模式下开启tree shaking,主要是为了开发环境学习,可以放到prod环境下
new webpack.optimize.ModuleConcatenationPlugin(),
].filter(Boolean)
})
同时还需要在页面入口添加如下代码
import React from 'react'
import ReactDom from 'react-dom'
import App from './app'
if (module.hot) {
module.hot.accept('./app.jsx', function () {
const App = require("./app.jsx").default;
ReactDom.render(<App />, document.getElementById('app'))
})
}
ReactDom.render(<App />, document.getElementById('app'))
配置完成、页面代码开发后,将代码push到自己的git仓库就可以了
4、阿里云部署
大体方案是云服务器通过git将代码拉下来,利用Nginx来完成静态资源的转发
4.1、安装环境
通过阿里云控制台登录自己的机器,安装以下工具
1、git
yum install git
配置Git ssh-key
2、nvm
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
3、node
nvm install node版本
4、nginx
sudo yum install nginx
4.2、代码部署
1、利用git拉取之前push的代码
git clone https://github.com/MirrorHuang/webpack-demo.git
然后进入项目目录,执行npm run build
,执行结束后会有个dist目录,里面就是构建好的代码
2、Nginx配置
首先进入 nginx目录下
cd /etc/nginx/
修改nginx配置文件
vim nginx.conf
修改nginx权限,修改首行为user root
修改server
server {
root /root/web;
location / {
root /root/web/webpack-demo/dist/;
}
当浏览器输入www.xxx.com/pageA/pageA.html,会去/root/web/webpack-demo/dist/目录下找pageA/pageA.html文件,这样就可以访问到刚才构建完成的页面了
3、阿里云安全组
你以为一切都结束了??满怀欢喜的打开浏览器,输入url。。。。
然而现实往往是残酷的,还需要设置阿里云安全组,允许公网访问80端口(http默认端口)和443端口(https默认端口)
进入阿里云控制台,从左侧菜单栏中找到网络与安全->安全组,然后进入安全组,点击快速创建规则即可,具体可参考允许公网通过HTTP、HTTPS等服务访问实例
完成后输入url即可访问静态网页了,要是有域名的朋友还可以去设置域名解析
结语
该方案还是太粗糙,太依赖人为操作了,后续还会慢慢优化。。。
参考
webpack中的path、publicPath和contentBase