Как создать шаблон html для страницы stati c из верхнего / нижнего колонтитула (и css / js inject) в Webpack 4 (шаблон loda sh не работает)? - PullRequest
1 голос
/ 23 апреля 2020

Я пытаюсь создать страницы c html с помощью веб-пакета 4. Я использую html -webpack-plugin . Я хочу определить заголовок . html и нижний колонтитул. html и импортировать их позже на все html страницы. Но я также хочу, чтобы мои выходные файлы js и css автоматически вводились в этот верхний и нижний колонтитулы. Примерно так:

header. html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Some Title</title>
    <% for(var i=0; i < htmlWebpackPlugin.files.css.length; i++) {%>
        <link type="text/css" rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[i] %>">
    <% } %>
</head>
<body>

нижний колонтитул. html:

    <% for(var i=0; i < htmlWebpackPlugin.files.js.length; i++) {%>
        <script type="text/javascript" src="<%= htmlWebpackPlugin.files.js[i] %>"></script>
    <% } %>
</body>
</html>

index.hbs (или index. html):

<%= _.template(require('./../includes/header.html'))() %>

    <div class="content">
        <img src="img/bg/desktop/bg-index-03.jpg" width="500" height="500"/>
        <div class="imgtest"></div>
    </div> <!-- .content -->

<%= _.template(require('./../includes/footer.html'))() %>

Но я получаю в dist / index. html некоторую ошибку. Эти строки все те же и не загружают верхний / нижний колонтитул в индекс. html:

<%= _.template(require('./../includes/header.html'))() %>
...
<%= _.template(require('./../includes/footer.html'))() %>

По некоторым причинам loda sh не работает ...

UPD :

Я изменил src/html/views/index.hbs на src/html/views/index.html (и в плагине)

new HtmlWebpackPlugin({
  template: './src/html/views/index.html',
...

Добавить include строку здесь:

{
    test: /\.html$/,
    include: path.resolve(__dirname, 'src/html/includes'),
    use: ['html-loader']
},

В верхнем / нижнем колонтитуле Я удалил loda sh код <% ... %>, оставив только чистый html - так оно и работает! Верхний / нижний колонтитул импортирован в index. html, но без css / js.

Если я верну loda sh обратно в нижний колонтитул. html (или header. html)

<% for(var i=0; i < htmlWebpackPlugin.files.js.length; i++) {%>
    <script type="text/javascript" src="<%= htmlWebpackPlugin.files.js[i] %>"></script>
<% } %>

Я получаю ошибку:

ERROR in Template execution failed: ReferenceError: htmlWebpackPlugin is not defined

ERROR in   ReferenceError: htmlWebpackPlugin is not defined

  - lodash.templateSources[2]:10 eval
    lodash.templateSources[2]:10:19

  - index.html:102 
    D:/.../src/html/views/index.html:102:110

  - index.html:104 ./node_modules/html-webpack-plugin/lib/loader.js!./src/html/views/index.html.module.exports
    D:/.../src/html/views/index.html:104:3

  - index.js:393 
    [project]/[html-webpack-plugin]/index.js:393:16

  - runMicrotasks

  - task_queues.js:93 processTicksAndRejections
    internal/process/task_queues.js:93:5

  - async Promise.all

Почему это происходит? Что не так? Как заставить loda sh работать в верхнем / нижнем колонтитуле?

Или каков наилучший способ создания html шаблонов?

дерево файлов:

dist
│   index.html
├───css
│       main.css
│       main.css.map
├───fonts
├───img
├───js
│       main.js
│       main.js.map
│       vendors~main.js
│       vendors~main.js.map
src
├───favicon
├───fonts
├───html
│   ├───includes
│   │       footer.html
│   │       header.html
│   └───views
│           index.hbs
├───img
├───js
│       index.js
├───scss
│       fonts.scss
│       icomoon.scss
│       style.scss
package.json
package-lock.json
webpack.config.js

webpack.config. js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const OptimizeCssAssetWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

const isDev = process.env.NODE_ENV === 'development';
const isProd = !isDev;

const optimization = () => {
  const config = {
    splitChunks: {
      chunks: 'all'
    }
  }
  if (isProd) {
    config.minimizer = [
      new OptimizeCssAssetWebpackPlugin(),
      new TerserWebpackPlugin()
    ]
  }
  return config
}

const filename = ext => isDev ? `[name].${ext}` : `[name].[hash].${ext}`;

const cssLoaders = extra => {
  const loaders = [
    {
      loader: MiniCssExtractPlugin.loader,
      options: {
        hmr: isDev,
        reloadAll: true
      },
    },
    {
      loader: 'css-loader',
      options: {
        url: false
      }
    }
  ];
  if (extra) {
    loaders.push(extra)
  }
  return loaders
}

const babelOptions = preset => {
  const opts = {
    presets: [
      '@babel/preset-env'
    ],
    plugins: [
      '@babel/plugin-proposal-class-properties'
    ]
  }

  if (preset) {
    opts.presets.push(preset)
  }

  return opts
}

module.exports = {
  mode: 'development',
  entry: [
    '@babel/polyfill',
    './src/js/index.js'
  ],
  output: {
    filename: 'js/' + filename('js'),
    path: path.resolve(__dirname, 'dist')
  },
  devServer: {
    port: 4250,
    hot: isDev
  },
  devtool: isDev ? 'source-map' : '',
  resolve: {
    //extensions: ['.js', '.json', '.png'],
    alias: {
      '@views': path.resolve(__dirname, 'src/html/views'),
      '@includes': path.resolve(__dirname, 'src/html/includes'),
      '@scss': path.resolve(__dirname, 'src/scss'),
      '@': path.resolve(__dirname, 'src'),
    }
  },
  optimization: optimization(),
  module: {
    rules: [
      {
        test: /\.html$/,
        //include: path.resolve(__dirname, 'src/html/includes'),
        use: ['html-loader']
      },
      {
        test: /\.hbs$/,
        loader: 'handlebars-loader'
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: {
          loader: 'babel-loader',
          options: babelOptions()
        }
      },
      {
        test: /\.css$/,
        use: cssLoaders()
      },
      {
        test: /\.s[ac]ss$/,
        use: cssLoaders('sass-loader')
      },
      {
        test: /\.(png|jpg|svg|gif)$/,
        exclude: path.resolve(__dirname, 'src/fonts'),
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
              outputPath: path.resolve(__dirname, 'dist/img')
            }
          }
        ]
      },
      {
        test: /\.(ttf|otf|svg|woff|woff2|eot)$/,
        exclude: path.resolve(__dirname, 'src/img'),
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
              outputPath: path.resolve(__dirname, 'dist/fonts')
            }
          }
        ]
      },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/html/views/index.hbs',
      minify: {
        collapseWhitespace: isProd
      },
      inject: false
    }),
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: 'css/' + filename('css'),
    }),
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, 'src/favicon'),
        to: path.resolve(__dirname, 'dist')
      },
      {
        from: path.resolve(__dirname, 'src/fonts'),
        to: path.resolve(__dirname, 'dist/fonts')
      },
      {
        from: path.resolve(__dirname, 'src/img'),
        to: path.resolve(__dirname, 'dist/img')
      }
    ])
  ]
};

пакет. json:

{
  "name": "Name",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack --mode development",
    "build": "cross-env NODE_ENV=production webpack --mode production",
    "watch": "cross-env NODE_ENV=development webpack --mode development --watch",
    "start": "cross-env NODE_ENV=development webpack-dev-server --mode development --open"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.9.0",
    "@babel/plugin-proposal-class-properties": "^7.8.3",
    "@babel/preset-env": "^7.9.5",
    "babel-loader": "^8.1.0",
    "clean-webpack-plugin": "^3.0.0",
    "copy-webpack-plugin": "^5.1.1",
    "cross-env": "^7.0.2",
    "css-loader": "^3.5.2",
    "file-loader": "^6.0.0",
    "html-loader": "^1.1.0",
    "html-webpack-plugin": "^4.2.0",
    "mini-css-extract-plugin": "^0.9.0",
    "node-sass": "^4.13.1",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "raw-loader": "^4.0.1",
    "resolve-url-loader": "^3.1.1",
    "sass-loader": "^8.0.2",
    "style-loader": "^1.1.4",
    "terser-webpack-plugin": "^2.3.5",
    "webpack": "^4.42.1",
    "webpack-cli": "^3.3.11",
    "webpack-dev-server": "^3.10.3"
  },
  "browserslist": "defaults",
  "dependencies": {
    "@babel/polyfill": "^7.8.7",
    "bootstrap": "^4.4.1",
    "handlebars": "^4.7.6",
    "handlebars-loader": "^1.7.1",
    "jquery": "^3.5.0",
    "popper.js": "^1.16.1"
  }
}

1 Ответ

0 голосов
/ 24 апреля 2020

Решено!

Я изменил inject на true в HtmlWebpackPlugin.

И я также добавил функцию generateHtmlPlugins() для автоматического добавления всех html -файлов в плагин от ./src/html/views.

src / html / includes / header. html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Some Title</title>
</head>
<body>
<header>header</header>

src / html / includes / footer. html:

    <footer>footer</footer>
</body>
</html>

src / html / views / index. html:

<%= _.template(require('./../includes/header-main.html'))() %>
    <div class="content">
        <img src="img/bg/desktop/bg-index-03.jpg" width="500" height="500"/>
        <div class="imgtest"></div>
    </div>
<%= _.template(require('./../includes/footer.html'))() %>

dist / index. html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
    <title>Some Title</title>
<link href="css/main.43f2075150009f972ed4.css" rel="stylesheet"></head>
<body>
<header>header</header>
    <div class="content">
        <img src="img/bg/desktop/bg-index-03.jpg" width="500" height="500">
        <div class="imgtest"></div>
    </div>
    <footer>footer</footer>
<script src="js/vendors~main.43f2075150009f972ed4.js"></script><script src="js/main.43f2075150009f972ed4.js"></script></body>
</html>

webpack.config. js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const OptimizeCssAssetWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const fs = require('fs');

const isDev = process.env.NODE_ENV === 'development';
const isProd = !isDev;

function generateHtmlPlugins(templateDir) {
  const templateFiles = fs.readdirSync(path.resolve(__dirname, templateDir));
  return templateFiles.map(item => {
    const parts = item.split('.');
    const name = parts[0];
    const extension = parts[1];
    return new HtmlWebpackPlugin({
      filename: `${name}.html`,
      template: path.resolve(__dirname, `${templateDir}/${name}.${extension}`),
      minify: {
        collapseWhitespace: isProd
      },
      inject: true,
    })
  })
}

const htmlPlugins = generateHtmlPlugins('./src/html/views')

const optimization = () => {
  const config = {
    splitChunks: {
      chunks: 'all'
    }
  }
  if (isProd) {
    config.minimizer = [
      new OptimizeCssAssetWebpackPlugin(),
      new TerserWebpackPlugin()
    ]
  }
  return config
}

const filename = ext => isDev ? `[name].${ext}` : `[name].[hash].${ext}`;

const cssLoaders = extra => {
  const loaders = [
    {
      loader: MiniCssExtractPlugin.loader,
      options: {
        hmr: isDev,
        reloadAll: true
      },
    },
    {
      loader: 'css-loader',
      options: {
        url: false
      }
    }
  ];
  if (extra) {
    loaders.push(extra)
  }
  return loaders
}

const babelOptions = preset => {
  const opts = {
    presets: [
      '@babel/preset-env'
    ],
    plugins: [
      '@babel/plugin-proposal-class-properties'
    ]
  }

  if (preset) {
    opts.presets.push(preset)
  }

  return opts
}

module.exports = {
  mode: 'development',
  entry: [
    '@babel/polyfill',
    './src/js/index.js'
  ],
  output: {
    filename: 'js/' + filename('js'),
    path: path.resolve(__dirname, 'dist')
  },
  devServer: {
    port: 4250,
    hot: isDev
  },
  devtool: isDev ? 'source-map' : '',
  resolve: {
    //extensions: ['.js', '.json', '.png'],
    alias: {
      '@views': path.resolve(__dirname, 'src/html/views'),
      '@includes': path.resolve(__dirname, 'src/html/includes'),
      '@scss': path.resolve(__dirname, 'src/scss'),
      '@': path.resolve(__dirname, 'src'),
    }
  },
  optimization: optimization(),
  module: {
    rules: [
      {
        test: /\.html$/,
        include: path.resolve(__dirname, 'src/html/includes'),
        loader: 'html-loader',
        options: {
          minimize: false,
        }
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: {
          loader: 'babel-loader',
          options: babelOptions()
        }
      },
      {
        test: /\.css$/,
        use: cssLoaders()
      },
      {
        test: /\.s[ac]ss$/,
        use: cssLoaders('sass-loader')
      },
      {
        test: /\.(png|jpg|svg|gif)$/,
        exclude: path.resolve(__dirname, 'src/fonts'),
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
              outputPath: path.resolve(__dirname, 'dist/img')
            }
          }
        ]
      },
      {
        test: /\.(ttf|otf|svg|woff|woff2|eot)$/,
        exclude: path.resolve(__dirname, 'src/img'),
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
              outputPath: path.resolve(__dirname, 'dist/fonts')
            }
          }
        ]
      },
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: 'css/' + filename('css'),
    }),
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, 'src/favicon'),
        to: path.resolve(__dirname, 'dist')
      },
      {
        from: path.resolve(__dirname, 'src/fonts'),
        to: path.resolve(__dirname, 'dist/fonts')
      },
      {
        from: path.resolve(__dirname, 'src/img'),
        to: path.resolve(__dirname, 'dist/img')
      }
    ])
  ].concat(htmlPlugins)
};
...