阿里云从0到1部署静态网站

359 阅读5分钟

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

webpack中的hash、chunkhash、contenthash区别

Linux安装Nginx正确方式

Nginx Serving Static Content