项目目录结构如下:
config文件夹内代码如下:
index.js:
module.exports = { dev: { assetsSubDirectory: 'static', assetsPublicPath: '', devtool: 'cheap-module-eval-source-map', proxy: { '/api': { target: 'http://localhost:8888', changeOrigin: true, pathRewrite: { '^/api': '/api' } } }, // host: 'localhost', host:'0.0.0.0', port: 8888 }, prod: { assetsSubDirectory: 'static', assetsPublicPath: './', devtool: 'source-map' }, templatePc(data) { //PC sprite template return data.sprites.map(sprite => { return ( `.icon-${sprite.name} { ${sprite.px.width}; height: ${sprite.px.height}; background: url(${sprite.image}) ${sprite.px.offset_x} ${sprite.px.offset_y} no-repeat; background-size: ${sprite.px.total_width} ${sprite.px.total_height}; } `) }).join('') }, templateWeb(data) { //WEB sprite template return data.sprites.map(sprite => { return ( `.icon-${sprite.name} { ${sprite.width}pr; height: ${sprite.height}pr; background: url(${sprite.image}) ${sprite.offset_x}pr ${sprite.offset_y}pr no-repeat; background-size: ${sprite.total_width}pr ${sprite.total_height}pr; } `) }).join('') } }
utils.js内代码如下:
const glob = require('glob') // const path = require('path') exports.getEntry = function () { const entry = {} // 读取src目录所有page入口 glob.sync('../src/pages/*/*.js').forEach((name) => { const start = name.indexOf('src/') + 4; const end = name.length - 3; const eArr = []; const n = name.slice(start, end).split('/')[1]; eArr.push(name); // eArr.push('@babel/polyfill'); // 引入这个,是为了用async await,一些IE不支持的属性能够受支持,兼容IE浏览器用的 entry[n] = eArr; }) return entry; } exports.getEntry2 = function (globPath) { var entries = {}, tmp, pathname glob.sync(globPath).forEach(function (entry) { tmp = entry.split('/').splice(-3) pathname = tmp.splice(1, 1).toString().toLowerCase() entries[pathname] = entry }) return entries }
webpack.config.base.js:
const path = require('path') const config = require('./index') const SpritesmithPlugin = require('webpack-spritesmith') const utils = require('./utils') function resolve(dir) { return path.join(__dirname, '..', dir); } // const { getEntry } = require('./utils.js') module.exports = { context: path.resolve(__dirname, '../'), // entry:getEntry(), entry: utils.getEntry2("./src/pages/*/main.js"), // entry: { // main: './src/main.js' // }, // entry:{ // index:'./src/pages/index/main.js', // test:'./src/pages/test/main.js' // }, output: { path: path.resolve(__dirname, '../dist'), filename: '[name].js', publicPath: process.env.NODE_ENV === 'production' ? config.prod.assetsPublicPath : config.dev.assetsPublicPath }, resolve: { extensions: ['.js', '.json', 'jsx'], alias: { '@': resolve('src'), '@static': resolve('static') }, modules: ['node_modules', resolve('src/assets/images')] }, module: { rules: [ { test: /.jsx?$/, use: [ { loader: 'babel-loader', options: { cacheDirectory: true } }, { loader: 'eslint-loader', options: { formatter: require('eslint-friendly-formatter') } } ], exclude: [resolve('node_modules')], include: [resolve('src')] }, // { // test: /.html$/, // loader: 'html-loader' // }, { test: /.(png|jpe?g|gif|svg)(?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'images/[name]_[hash:8].[ext]' } }, { test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'media/[name]_[hash:8].[ext]' } }, { test: /.(woff2?|eot|ttf|otf)(?.*)?$/, loader: 'url-loader', options: { limit: 100000, name: 'fonts/[name]_[hash:8].[ext]' } } ] }, plugins: [ new SpritesmithPlugin({ src: { cwd: resolve('src/assets/images/icons'), glob: '*.png' }, target: { image: resolve('src/assets/images/sprite.png'), css: [ [resolve('src/assets/scss/_sprite.scss'), { format: 'based_template' }] ] }, customTemplates: { 'based_template': config.templateWeb }, apiOptions: { cssImageRef: '../images/sprite.png' }, spritesmithOptions: { algorithm: 'top-down', //'top-down', 'left-right', 'diagonal', 'alt-diagonal', 'binary-tree' padding: 10 } }) ], externals: { jquery: 'jQuery', swiper: 'Swiper' } }
webpack.config.base.js:
const path = require('path') const config = require('./index') const webpackBaseConfig = require('./webpack.config.base') const webpack = require('webpack') const merge = require('webpack-merge') const HtmlWebpackPlugin = require('html-webpack-plugin') const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin') const notifier = require('node-notifier') const portfinder = require('portfinder') const utils = require('./utils') var pages = utils.getEntry2('./src/pages/**/index.html'); var htmls = []; Object.keys(pages).forEach(name => { var templateUrl = pages[name]; var templateThunks = [name] htmls.push(new HtmlWebpackPlugin({ filename: name + '.html', template: templateUrl, // 模板路径 inject: true, chunks: templateThunks })) }) const webpackDevConfig = merge(webpackBaseConfig, { mode: 'development', module: { rules: [ { test: /.(sa|sc|c)ss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2, sourceMap: true } }, 'postcss-loader', 'sass-loader' ] } ] }, devtool: config.dev.devtool, devServer: { contentBase: path.join(__dirname, '../dist'), clientLogLevel: 'warning', historyApiFallback: true, hot: true, compress: true, host: config.dev.host, port: config.dev.port, open: true, overlay: { warnings: true, errors: true }, publicPath: config.dev.assetsPublicPath, proxy: config.dev.proxy, quiet: true }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), new webpack.NoEmitOnErrorsPlugin(), ...htmls // new HtmlWebpackPlugin({ // filename: 'index.html', // template: './src/pages/index/index.html', // inject: true, // chunks:["index"] // }), // new HtmlWebpackPlugin({ // filename: 'test.html', // template: './src/pages/test/index.html', // inject: true, // chunks:["test"] // }) ] }) module.exports = new Promise((resolve, reject) => { portfinder.basePort = config.dev.port portfinder.getPort((err, port) => { if (err) { reject(err) } else { webpackDevConfig.devServer.port = port webpackDevConfig.plugins.push(new FriendlyErrorsWebpackPlugin({ compilationSuccessInfo: { messages: [`You application is running here ${webpackDevConfig.devServer.host}:${port}`] }, onErrors: (severity, errors) => { if (severity !== 'error') { return } const error = errors[0] const filename = error.file && error.file.split('!').pop() notifier.notify({ title: error.name, subtitle: filename || '', message: severity + ': ' + error.message }); } })) resolve(webpackDevConfig) } }) })
webpack.config.prod.js:
const path = require('path') const config = require('./index') const webpackBaseConfig = require('./webpack.config.base') const merge = require('webpack-merge') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') //extract css const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') //compress css const TerserPlugin = require('terser-webpack-plugin') //compress js // const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin const utils = require('./utils') var pages = utils.getEntry2('./src/pages/**/index.html'); var htmls = []; Object.keys(pages).forEach(name => { var templateUrl = pages[name]; var templateThunks = [name] htmls.push(new HtmlWebpackPlugin({ filename: name + '.html', template: templateUrl, // 模板路径 inject: true, chunks: templateThunks })) }) const webpackProdConfig = merge(webpackBaseConfig, { mode: 'development', output: { path: path.resolve(__dirname, '../dist'), filename: 'js/[name]_[chunkhash:8].js', chunkFilename: 'js/[id]_[chunkhash:8].js', publicPath: config.prod.assetsPublicPath }, module: { rules: [ { test: /.(sa|sc|c)ss$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: '../' } }, { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader' ] }, { test: /.(png|jpe?g|gif|svg)(?.*)?$/, loader: 'image-webpack-loader', enforce: 'pre' } ] }, devtool: config.prod.devtool, optimization: { concatenateModules: true, splitChunks: { chunks: 'all' }, minimizer: [ new OptimizeCssAssetsPlugin(), new TerserPlugin({ parallel: true, cache: true, terserOptions: { compress: { unused: true, drop_debugger: true, drop_console: true, dead_code: true } } }), ], }, plugins: [ new CleanWebpackPlugin(), new MiniCssExtractPlugin({ filename: 'css/[name]_[contenthash:8].css', chunkFilename: 'css/[name]_[contenthash:8].css', }), new OptimizeCssAssetsPlugin({ assetNameRegExp: /.optimize.css$/g, cssProcessor: require('cssnano'), cssProcessorPluginOptions: { preset: ['default', { discardComments: { removeAll: true } }], }, canPrint: true }), ...htmls, // new HtmlWebpackPlugin({ // filename: '../dist/index.html', // template: 'src/index.html', // inject: true // }), new CopyWebpackPlugin([{ from: path.resolve(__dirname, '../static'), to: config.dev.assetsSubDirectory, ignore: ['.*'] }]), // new BundleAnalyzerPlugin() ] }) module.exports = webpackProdConfig
package.json内script代码
"scripts": { "dev": "webpack-dev-server --inline --progress --config config/webpack.config.dev.js", "start": "npm run dev", "build": "webpack --progress --config config/webpack.config.prod.js" },
1、使用第三方库
1.1需要用到第三方库时需在页面引入,比如jq
1.2同时还在配置文件中解析
externals: { jquery: 'jQuery', swiper: 'Swiper' }
1.3最后在页面对应的main.js里使用时采用如下语法
import $ from 'jquery' $(".close_ly").click(function(){ $(".leyu_yspd,.toumingbi_yspd").hide() }) $(".head_onlineChat").click(function(){ $(".leyu_yspd,.toumingbi_yspd").show() })
2、引入样式
2.1直接在页面内对应的main.js里
import "@/assets/scss/select.scss"
3、引用组件
3.1在页面内使用
<%= require('html-loader!../../components/header.html') %>
注意这是EJS的语法,一定不能使用html-loader(html-loader会将模版以字符串方式解析)
// { // test: /.html$/, // loader: 'html-loader' // },
4、图片引入
4.1页面引入图片
<img src="<%= require('../../assets/images/07.png') %>" alt="">
4.2组件内引入图片
<img src="../assets/images/03.png" alt="">