この記事では、前の記事で残された 2 つの問題を解決する必要があります:
webpack がエントリ ファイルを自動的に検出し、対応するエントリ ファイルを設定する方法template
バックエンド テンプレートでスタイルとスクリプトの自動導入を直接処理する方法
Express プロジェクトを例として、express-generator を使用して初期プロジェクトを構築し、必要なディレクトリを追加します。最終的なディレクトリ構造は次のとおりです。
- website - bin #express项目启动文件 - lib #express项目开发所需的库 + routes #express项目路由 - src #前端源码开发目录 - styles #css目录,按照页面(模块)、通用、第三方三个级别进行组织 + page + common + lib + imgs #图片资源 - scripts #JS脚本,按照page、components进行组织 + page + components + views #HTML模板 - public #webpack编译打包输出目录的静态文件,express工程的静态目录 + styles + scripts + imgs + views #webpack编译输出的模板静态文件,express工程的视图模板 + node_modules #所使用的nodejs模块 package.json #项目配置 webpack.config.js #webpack配置 README.md #项目说明
を自由に設計することもできます。ディレクトリ構造は個人の好みに応じて変更できます。完全なソース コードの例については、https://github.com/vhtml/webpack-MultiPage にアクセスしてください。
package.json で最後に宣言された依存関係は次のとおりです:
"devDependencies": { "css-loader": "^0.23.1", "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.8.5", "glob": "^7.0.0", "html-loader": "^0.4.3", "html-webpack-plugin": "^2.9.0", "jquery": "^1.12.0", "less": "^2.6.0", "less-loader": "^2.2.2", "style-loader": "^0.13.0", "url-loader": "^0.5.7", "webpack": "^1.12.13", "webpack-dev-server": "^1.14.1"}
ご覧のとおり、glob 依存関係がもう 1 つあります。前回の記事より パターンマッチングに基づいてファイルの一覧を取得するノードモジュールです。 glob の詳しい使用方法は、https://github.com/isaacs/node-glob でご覧いただけます。 glob モジュールを使用すると、src/scripts/page パスにあるすべての js エントリ ファイルを簡単に取得できます。同様に、エントリーファイルに対応したテンプレート構成を自動で設定することができます。
最終的な webpack 設定は次のとおりです。
var path = require('path');var glob = require('glob');var webpack = require('webpack');var ExtractTextPlugin = require('extract-text-webpack-plugin');var HtmlWebpackPlugin = require('html-webpack-plugin');var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;const debug = process.env.NODE_ENV !== 'production';var entries = getEntry('src/scripts/page/**/*.js', 'src/scripts/page/');var chunks = Object.keys(entries);var config = { entry: entries, output: { path: path.join(__dirname, 'public'), publicPath: '/static/', filename: 'scripts/[name].js', chunkFilename: 'scripts/[id].chunk.js' }, module: { loaders: [ //加载器 { test: /\.css$/, loader: ExtractTextPlugin.extract('style', 'css') }, { test: /\.less$/, loader: ExtractTextPlugin.extract('css!less') }, { test: /\.html$/, loader: "html" }, { test: /\.(woff|woff2|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file-loader?name=fonts/[name].[ext]' }, { test: /\.(png|jpe?g|gif)$/, loader: 'url-loader?limit=8192&name=imgs/[name]-[hash].[ext]' } ] }, plugins: [ new webpack.ProvidePlugin({ //加载jq $: 'jquery' }), new CommonsChunkPlugin({ name: 'vendors', // 将公共模块提取,生成名为`vendors`的chunk chunks: chunks, minChunks: chunks.length // 提取所有entry共同依赖的模块 }), new ExtractTextPlugin('styles/[name].css'), //单独使用link标签加载css并设置路径,相对于output配置中的publickPath debug ? function() {} : new UglifyJsPlugin({ //压缩代码 compress: { warnings: false }, except: ['$super', '$', 'exports', 'require'] //排除关键字 }), new webpack.HotModuleReplacementPlugin() //热加载 ], devServer: { publicPath:'http://localhost:8080/static/', proxy: { "*": "http://localhost:54999" }, inline: true, hot: true }};var pages = Object.keys(getEntry('src/views/**/*.html', 'src/views/'));pages.forEach(function(pathname) { var conf = { filename: '../views/' + pathname + '.html', //生成的html存放路径,相对于path template: 'src/views/' + pathname + '.html', //html模板路径 inject: false, //js插入的位置,true/'head'/'body'/false minify: { //压缩HTML文件 removeComments: true, //移除HTML中的注释 collapseWhitespace: false //删除空白符与换行符 } }; if (pathname in config.entry) { conf.inject = 'body'; conf.chunks = ['vendors', pathname]; conf.hash = true; } config.plugins.push(new HtmlWebpackPlugin(conf));});module.exports = config;function getEntry(globPath, pathDir) { var files = glob.sync(globPath); var entries = {}, entry, dirname, basename, pathname, extname; for (var i = 0; i < files.length; i++) { entry = files[i]; dirname = path.dirname(entry); extname = path.extname(entry); basename = path.basename(entry, extname); pathname = path.join(dirname, basename); pathname = pathDir ? pathname.replace(new RegExp('^' + pathDir), '') : pathname; entries[pathname] = './' + entry; } return entries;}
ここでは、バックエンドの質問。私も当初、この問題を熱心に調査しましたが、それは不可能かもしれないと感じ、最初に純粋な静的パッケージを使用してから、それをバックエンドのテンプレートに書き直すことを計画しました。そのようなローダーは存在しないため、テンプレート インクルードの問題を非常にインテリジェントに処理し、非 HTML テンプレートに CSS と JS を自動的に導入します)。でも、これをやると本当に痛いんです!明らかに 1 つのものであるのに、なぜ 2 つのものに分割する必要があるのでしょうか? !
このような探索を行ったことがある場合は、jade-loader、ejs-loader、ejs-compiled-loader などの Webpack ローダーに遭遇したことがあるかもしれません。残念ながら、それらはどれも私が探しているものではありません。テンプレートの元の生態を保持せずにテンプレートをコンパイルするだけであり、CSS と JS を自動的に導入することはできません。また、ejs テンプレートを HTML テンプレートに変換し (include タグのみを処理し、残りはそのままにしておく)、それを処理するために html-loader を使用する独自のローダーを作成しようとしましたが、これによりテンプレートの再利用性が破壊され、失われてしまいました。柔軟性。
はい、実際には、src/views にテンプレートをそのまま出力し、前回の記事のように css と js を自動的にインポートしたいだけです。これですべてです。思いがけず、行き詰まりそうになり、複雑に考えすぎてしまいました。
html-webpack-plugin プラグインは、css と js を解析して、テンプレート内の対応する head タグと body タグのペアに挿入することで、自動的に導入する原則を実装しているという事実を最初に知っておく必要があります。 head タグと body タグがない場合、テンプレートの先頭と末尾にこれら 2 つのタグがそれぞれ生成され、CSS と JS を導入するための link タグと script タグが挿入されます。テンプレートに何を書いても構いません。この原則を理解すれば、「大義」を達成することも遠くありません。まず、テンプレートの書き換え方法を変更する必要があります。テンプレートの構造は、jade ではなく html に似ている必要があります (幸いなことに、私は jade を使いたくないのですが)。次のように、artTemplate テンプレートを例に挙げます。
<!DOCTYPE html><html><head> {{include './common/meta'}}</head><body> {{include './common/header'}} <div class="g-bd"> {{include './common/_content'}} </div> {{include './common/footer'}}</body></html>
はい、その通りです。ただし、完全な頭と体の構造が保持されている限りは可能です。次に、上記の Webpack 設定に従って、エントリ js に対応するテンプレートを link タグと script タグに挿入し、./views ディレクトリに出力します。残りのテンプレートは、そのまま ./views ディレクトリまたは対応するサブディレクトリに出力できます。 。
この時点で、「大義」は完了します。
より良い解決策がある場合は、共有してください。