Webpack4 멀티 페이지 어플리케이션 패키징 시나리오 상세 정보

앞말
웹팩을 배운 후에 자신의 블로그의 앞부분을 재구성했다. 자신의 블로그는 여러 페이지 응용이기 때문에 다음 여러 페이지 응용의 패키지 방안을 연구했다.여기서 마지막 배치의 성과를 공유하고 부족한 점을 지적해 주십시오.(문자가 많지 않고 모두 코드이며 설정 강좌가 아니기 때문에 특별히 상세하게 쓰지 않고 참고일 뿐)
프로젝트 주소:https://github.com/Ray-daydayup/MPA-webpack
파일 디렉토리 구조
프로젝트 디렉토리 구조
우선 제 프로젝트의 디렉터리 구조를 보겠습니다.

myblog-webpack
 ├── dist           //  
 ├── src           //  
 │  ├── api         //  , 
 │  ├── assets        //  
 │  ├── lib         //  
 │  ├── pages        //  
 │  │  ├── about      //  
 │  │  │  ├── index.html  // html 
 │  │  │  └── index.js   //  js
 │  │  ├── category
 │  │  │  ├── index.html
 │  │  │  └── index.js
 │  │  ├── detail
 │  │  │  ├── index.html
 │  │  │  └── index.js
 │  │  ├── index
 │  │  │  ├── index.html
 │  │  │  └── index.js
 │  │  └── tag
 │  │    ├── index.html
 │  │    └── index.js
 │  ├── styles       //  
 │  └── utils        //  
 ├── package.json
 ├── README.md
 ├── package-lock.json
 ├── webpack.base.js    //  
 ├── webpack.dev.js     //  
 └── webpack.prod.js    //  
패키지 출력 파일 디렉터리 구조
최종 패키지 출력 디렉터리는 다음과 같습니다.

dist
 ├── assets
 ├── css
 │  ├── commons672a1e57.css
 │  └── index6085d612.css
 ├── js
 │  ├── aboutfc723f0e.js
 │  ├── categorye4be3bd6.js
 │  ├── commons78f1dd3f.js
 │  ├── detail0df434c5.js
 │  ├── indexe1e985d9.js
 │  ├── markdown-it
 │  │  ├── highlight.vendors.js
 │  │  ├── markdown-it-integrated.js
 │  │  ├── markdown-it-integrated.min.js
 │  │  ├── markdown-it-integrated.min.js.LICENSE.txt
 │  │  └── markdown-it.vendors.js
 │  └── tagf1c1035c.js
 ├── about.html
 ├── category.html
 ├── detail.html
 ├── favicon.ico
 ├── index.html
 └── tag.html
웹 팩 프로필
주의: 문장의 각 부분에 필요한 가방 이름은 쓰지 않았습니다. 부록에서 찾으세요!!!관련 설정의 의미는 공식 문서를 찾을 수 있습니다
공통 구성
동적 가져오기 entry 및 html-webpack-plugin 설정
다중 페이지 응용의 패키지 사고방식은 모든 페이지가 하나의entry, 하나의html-webpack-plugin에 대응하지만 페이지를 추가하거나 삭제할 때마다 웹pack 설정을 바꿔야 하기 때문에 파일 디렉터리에 따라 동적으로 입구를 가져와 html-webpack-plugin을 설정해야 한다.
라이브러리 glob를 이용하여 파일 디렉터리에 대한 구체적인 코드를 다음과 같이 읽을 수 있습니다. 관련 설정의 의미는 공식 문서를 찾을 수 있습니다.

const path = require('path')
const glob = require('glob')
const HtmlWebpackPlugin = require('html-webpack-plugin')

const setMPA = () => {
 const entry = {}
 const htmlWebpackPlugins = []
 const entryFiles = glob.sync(path.join(__dirname, './src/pages/*/index.js')) //  
 entryFiles.forEach((item) => {
  const pageName = item.match(/pages\/(.*)\/index.js/)[1] //  , 
  entry[pageName] = item                  //  
  //  html-webpack-plugin 
  htmlWebpackPlugins.push(
   new HtmlWebpackPlugin({
    template: path.join(__dirname, `./src/pages/${pageName}/index.html`),// 
    filename: `${pageName}.html`, // 
    chunks: [pageName], //  js chunk , output 
    inject: true,    
    favicon: path.join(__dirname, './src/assets/favicon.ico'), //  
    minify: { // 
     html5: true,
     collapseWhitespace: true,
     preserveLineBreaks: false,
     minifyCSS: true,
     minifyJS: true,
     removeComments: false
    }
   })
  )
 })
 return {
  entry,
  htmlWebpackPlugins
 }
}

const { entry, htmlWebpackPlugins } = setMPA()
ES6 및 async, await, eslint 사용
ES6와 async, await 및 eslint를 사용하려면 babel-loader와 eslint-loader를 빌려야 합니다. 구체적인 설정은 간단합니다.
babel 관련 라이브러리: @babel/core, @babel/preset-env, @babel/plugin-transform-regenerator, @babel/plugin-transform-runtime, eslint 관련 라이브러리: babel-eslint, eslint
module.rules 구성

{
  test: /\.js$/,
  use: ['babel-loader', 'eslint-loader']
}
.babelrc 파일 설정

{
 "presets": [
  "@babel/preset-env"
 ],
 "plugins": [ // async await
  "@babel/plugin-transform-runtime",
  "@babel/plugin-transform-regenerator"
 ]
}
.eslintrc.js의 설정

module.exports = {
 parser: 'babel-eslint',
 extends: ['alloy'], // eslint 
 env: {
  browser: true,
  node: true
 },
 rules: {
  'no-new': 'off',
  'no-proto': 'off'
  // 'no-console': 'error'
 }
}
그림 로드
url-loader를 이용하여 구체적인module.rules 설정은 다음과 같습니다.

{
  test: /\.(png|svg|jpg|gif)$/,
  use: [
   {
    loader: 'url-loader',
    options: {
     esModule: false,
     name: '[name][hash:8].[ext]', // 
     limit: 10240,         //  base64
     outputPath: './assets/images/' //  publicPath 
    }
   }
  ]
 }
패키지 디렉터리 정리 및 패키지 디렉터리로 파일 복사
패키지 디렉터리 자동 정리는clean-webpack-plugin 플러그인을 이용하고, 파일 복사는copy-webpack-plugin을 이용한다

const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')

const basePlugins = [
 ...htmlWebpackPlugins, // htmlWebpackPlugin 
 new CleanWebpackPlugin(), //  
 new CopyWebpackPlugin({
  patterns: [
   {
    from: path.join(__dirname, 'src/lib/markdown-it'), //  
    to: path.join(__dirname, 'dist/js/markdown-it') //  
   }
  ]
 })
]

.브라우저slistrc 설정

# Browsers that we support

last 2 version
> 1%
iOS 7

개발 환경 설정
핫 업데이트 및 devServer
구성은 다음과 같습니다.

plugins: [...basePlugins, new webpack.HotModuleReplacementPlugin()], // 
devServer: {
  contentBase: 'dist', //  
  hot: true, // 
  proxy: {
   '/api': {
    target: 'http://raydaydayup.cn:3000', // 
    pathRewrite: { '^/api': '' }
   }
  }
}
CSS 관련 구성
module.rules의 설정은 다음과 같습니다.

{
  test: /\.less$/,
  use: ['style-loader', 'css-loader', 'less-loader']
},
{
  test: /\.css$/,
  use: ['style-loader', 'css-loader']
}
출구

output: {
  filename: '[name].js',
  path: path.join(__dirname, 'dist')
}
운영 환경 구성
출구

output: {
  filename: 'js/[name][chunkhash:8].js', //  js 
  path: path.join(__dirname, 'dist'),
  publicPath: '/' //  
}
js 코드 압축
terser-webpack-plugin 플러그인을 이용하여optimization 설정

const TerserPlugin = require('terser-webpack-plugin')

optimization: {
  minimize: true,
  minimizer: [
   new TerserPlugin({
    include: [/\.js$/]
   })
  ]
}

CSS 관련 구성
CSS 코드 압축, optimize-css-assets-webpack-plugin 및 cssnano 활용

const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')

plugins: [
 new OptimizeCSSAssetsPlugin({
   assetNameRegExp: /\.css$/g,
   cssProcessor: require('cssnano')
  })
]

CSS는 단독 파일을 분리하고 mini-css-extract-plugin을 이용한다

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module: {
 rules: [
   ...baseModuleRules,
   {
    test: /\.less$/,
    use: [
     {
      loader: MiniCssExtractPlugin.loader,
      options: {
       esModule: true
      }
     },
     'css-loader',
     'less-loader'
    ]
   },
   {
    test: /\.css$/,
    use: [
     {
      loader: MiniCssExtractPlugin.loader,
      options: {
       esModule: true
      }
     },
     'css-loader'
    ]
   }
  ]
},
plugins: [
  ...basePlugins,
  new MiniCssExtractPlugin({
   filename: 'css/[name][contenthash:8].css' //  
  }),
 ],

PostCSS 플러그인 autoprefixer CSS3 접두사 자동 수정

module: {
  rules: [
   ...baseModuleRules,
   {
    test: /\.less$/,
    use: [
     {
      loader: MiniCssExtractPlugin.loader,
      options: {
       esModule: true
      }
     },
     'css-loader',
     {
      loader: 'postcss-loader',
      options: {
       plugins: () => [require('autoprefixer')]
      }
     },
     'less-loader'
    ]
   },
  ]
 },
공용 파일 분리(CSS 및 JS)
최적화를 설정합니다.splitChunks

optimization: {
  splitChunks: {
   cacheGroups: {
    commons: {
     name: 'commons',
     chunks: 'all',
     minChunks: 2 //  
    }
   }
  }
 }
사용 관련 주의
html-webpack-plugin 템플릿 사용 및 html-loader
html 세션을 가져오고 상응하는 위치에 다음 코드를 쓰는 것과 같이 주의해야 할 것은 html-loader가 불러온 html 세션 내부 <%>문법이 효력을 상실할 수 있음

<%= require("html-loader!@/components/recent.html") %>
html에서 그림을 불러옵니다.html에서 관련된img 라벨의 그림은 html-loader가 불러올 때 자동으로 url-loader를 호출합니다. 그러나 문제가 있습니다. 불러오는 경로에''가 없습니다.이 문제를 피하기 위해 저는 html-loader가 불러온 html 세션에서 그림을 사용하지 않고 직접 html 모드를 사용합니다
보드에서 직접require 사용하기

<img src="<%= require('@/assets/logo362x82.png') %>" alt="" />
부록
프로필 전체 버전
webpack.base.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin')

const path = require('path')
const glob = require('glob')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')

const setMPA = () => {
 const entry = {}
 const htmlWebpackPlugins = []
 const entryFiles = glob.sync(path.join(__dirname, './src/pages/*/index.js'))
 entryFiles.forEach((item) => {
  const pageName = item.match(/pages\/(.*)\/index.js/)[1]
  entry[pageName] = item
  htmlWebpackPlugins.push(
   new HtmlWebpackPlugin({
    template: path.join(__dirname, `./src/pages/${pageName}/index.html`),
    filename: `${pageName}.html`,
    chunks: [pageName],
    inject: true,
    favicon: path.join(__dirname, './src/assets/favicon.ico'),
    minify: {
     html5: true,
     collapseWhitespace: true,
     preserveLineBreaks: false,
     minifyCSS: true,
     minifyJS: true,
     removeComments: false
    }
   })
  )
 })
 return {
  entry,
  htmlWebpackPlugins
 }
}

const { entry, htmlWebpackPlugins } = setMPA()

const baseModuleRules = [
 {
  test: /\.js$/,
  use: ['babel-loader', 'eslint-loader']
 },
 {
  test: /\.(png|svg|jpg|gif)$/,
  use: [
   {
    loader: 'url-loader',
    options: {
     esModule: false,
     name: '[name][hash:8].[ext]',
     limit: 10240,
     outputPath: './assets/images/'
    }
   }
  ]
 }
]

const basePlugins = [
 ...htmlWebpackPlugins,
 new CleanWebpackPlugin(),
 new CopyWebpackPlugin({
  patterns: [
   {
    from: path.join(__dirname, 'src/lib/markdown-it'),
    to: path.join(__dirname, 'dist/js/markdown-it')
   }
  ]
 })
]
module.exports = {
 entry,
 baseModuleRules,
 basePlugins
}

webpack.dev.js

const webpack = require('webpack')
const path = require('path')
const { entry, baseModuleRules, basePlugins } = require('./webpack.base.js')

module.exports = {
 mode: 'development',
 entry,
 output: {
  filename: '[name].js',
  path: path.join(__dirname, 'dist')
 },
 module: {
  rules: [
   ...baseModuleRules,
   {
    test: /\.less$/,
    use: ['style-loader', 'css-loader', 'less-loader']
   },
   {
    test: /\.css$/,
    use: ['style-loader', 'css-loader']
   }
  ]
 },
 resolve: {
  //  
  alias: {
   '@': path.join(__dirname, 'src') //   @   src  
  }
 },
 plugins: [...basePlugins, new webpack.HotModuleReplacementPlugin()],
 devServer: {
  contentBase: 'dist',
  hot: true,
  proxy: {
   '/api': {
    target: 'http://raydaydayup.cn:3000',
    pathRewrite: { '^/api': '' }
   }
  }
 }
}

webpack.prod.js

const { entry, baseModuleRules, basePlugins } = require('./webpack.base.js')
const path = require('path')
const TerserPlugin = require('terser-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
 mode: 'production',
 entry,
 output: {
  filename: 'js/[name][chunkhash:8].js',
  path: path.join(__dirname, 'dist'),
  publicPath: '/'
 },
 resolve: {
  //  
  alias: {
   '@': path.resolve('src') //   @   src  
  }
 },
 module: {
  rules: [
   ...baseModuleRules,
   {
    test: /\.less$/,
    use: [
     {
      loader: MiniCssExtractPlugin.loader,
      options: {
       esModule: true
      }
     },
     'css-loader',
     {
      loader: 'postcss-loader',
      options: {
       plugins: () => [require('autoprefixer')]
      }
     },
     'less-loader'
    ]
   },
   {
    test: /\.css$/,
    use: [
     {
      loader: MiniCssExtractPlugin.loader,
      options: {
       esModule: true
      }
     },
     'css-loader'
    ]
   }
  ]
 },
 plugins: [
  ...basePlugins,
  new MiniCssExtractPlugin({
   filename: 'css/[name][contenthash:8].css'
  }),
  new OptimizeCSSAssetsPlugin({
   assetNameRegExp: /\.css$/g,
   cssProcessor: require('cssnano')
  })
 ],
 optimization: {
  minimize: true,
  minimizer: [
   new TerserPlugin({
    include: [/\.js$/]
   })
  ],
  splitChunks: {
   cacheGroups: {
    commons: {
     name: 'commons',
     chunks: 'all',
     minChunks: 2
    }
   }
  }
 }
}
package.json 파일

{
 "name": "myblog-webpack",
 "version": "1.0.0",
 "description": "",
 "main": ".eslintrc.js",
 "scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "build": "webpack --config webpack.prod.js",
  "dev": "webpack-dev-server --config webpack.dev.js --open"
 },
 "keywords": [],
 "author": "",
 "license": "ISC",
 "devDependencies": {
  "@babel/core": "^7.10.2",
  "@babel/plugin-transform-regenerator": "^7.10.3",
  "@babel/plugin-transform-runtime": "^7.10.3",
  "@babel/preset-env": "^7.10.2",
  "autoprefixer": "^9.8.4",
  "babel-eslint": "^10.1.0",
  "babel-loader": "^8.1.0",
  "clean-webpack-plugin": "^3.0.0",
  "copy-webpack-plugin": "^6.0.3",
  "css-loader": "^3.6.0",
  "cssnano": "^4.1.10",
  "eslint": "^7.2.0",
  "eslint-config-alloy": "^3.7.2",
  "eslint-loader": "^4.0.2",
  "file-loader": "^6.0.0",
  "glob": "^7.1.6",
  "html-loader": "^1.1.0",
  "html-webpack-plugin": "^4.3.0",
  "less-loader": "^6.1.2",
  "mini-css-extract-plugin": "^0.9.0",
  "optimize-css-assets-webpack-plugin": "^5.0.3",
  "postcss-loader": "^3.0.0",
  "style-loader": "^1.2.1",
  "terser-webpack-plugin": "^3.0.5",
  "url-loader": "^4.1.0",
  "webpack": "^4.43.0",
  "webpack-cli": "^3.3.11",
  "webpack-dev-server": "^3.11.0"
 },
 "dependencies": {
  "axios": "^0.19.2"
 }
}
Webpack4 멀티 페이지 앱 패키지 방안에 대한 상세한 설명은 여기까지입니다. 더 많은 관련 Webpack4 멀티 앱 패키지 내용은 이전의 글을 검색하거나 아래의 관련 글을 계속 훑어보십시오. 앞으로 많은 응원 부탁드립니다!

좋은 웹페이지 즐겨찾기