twitter-bootstrap 在 Webpack 中使用 Bootstrap 的首选方式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36131705/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 23:46:06  来源:igfitidea点击:

Preferred way of using Bootstrap in Webpack

javascripttwitter-bootstrapwebpackvue.jsbabel

提问by Nickvda

Greetings one and all,

向大家问好,

I have been playing around with Bootstrap for Webpack, but I'm at the point of tearing my hair out. I have literally gone through loads of blog articles and they either use the 7 months outdated 'bootstrap-webpack' plugin (which, surprisingly does not work out of the box) or.. They include the Bootstrap files through import 'node_modules/*/bootstrap/css/bootstrap.css'.

我一直在为 Webpack 使用 Bootstrap,但我已经迫不及待了。我确实浏览了大量的博客文章,他们要么使用了 7 个月过时的“bootstrap-webpack”插件(令人惊讶的是它不能立即使用),或者……它们通过 import 'node_modules/*/ 包含 Bootstrap 文件bootstrap/css/bootstrap.css'。

Surely, there must be a cleaner and more efficient way of going about this?

当然,必须有一种更清洁、更有效的方法来解决这个问题?

This is my current webpack.config.jsfile:

这是我当前的webpack.config.js文件:

var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var autoprefixer = require('autoprefixer');
var path = require('path');

module.exports = {
    entry: {
        app: path.resolve(__dirname, 'src/js/main.js')
    },
    module: {
        loaders: [{
            test: /\.js[x]?$/,
            loaders: ['babel-loader?presets[]=es2015&presets[]=react'],
            exclude: /(node_modules|bower_components)/
        }, {
            test: /\.css$/,
            loaders: ['style', 'css']
        }, {
            test: /\.scss$/,
            loaders: ['style', 'css', 'postcss', 'sass']
        }, {
            test: /\.sass$/,
            loader: 'style!css!sass?sourceMap'
        },{
            test: /\.less$/,
            loaders: ['style', 'css', 'less']
        }, {
            test: /\.woff$/,
            loader: "url-loader?limit=10000&mimetype=application/font-woff&name=[path][name].[ext]"
        }, {
            test: /\.woff2$/,
            loader: "url-loader?limit=10000&mimetype=application/font-woff2&name=[path][name].[ext]"
        }, {
            test: /\.(eot|ttf|svg|gif|png)$/,
            loader: "file-loader"
        }]
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '/js/bundle.js',
        sourceMapFilename: '/js/bundle.map',
        publicPath: '/'
    },
    plugins: [
        new ExtractTextPlugin('style.css')
    ],
    postcss: [
        autoprefixer({
            browsers: ['last 2 versions']
        })
    ],
    resolve: {
        extensions: ['', '.js', '.sass'],
        modulesDirectories: ['src', 'node_modules']
    },
    devServer: {
        inline: true,
        contentBase: './dist'
    }
};

I could go and require('bootstrap')(with some way of getting jQuery in it to work), but.. I'm curious to what you all think and do.

我可以去require('bootstrap')(通过某种方式让 jQuery 在其中工作),但是..我很好奇你们都在想什么和做什么。

Thanks in advance :)

提前致谢 :)

采纳答案by Saurabh

I am not sure if this is the best way, but following work for me well with vue.jswebapp. You can see the working code here.

我不确定这是否是最好的方法,但是使用vue.jswebapp对我来说很好。您可以在此处查看工作代码。

I have included files required by bootstrap in index.htmllike this:

我在index.html 中包含了 bootstrap 所需的文件,如下所示:

<head>
  <meta charset="utf-8">
  <title>Hey</title>
  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
  <link rel="stylesheet" href="/static/bootstrap.css" type="text/css">

  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js" integrity="sha384-XTs3FgkjiBgo8qjEjBk0tGmf3wPrWtA6coPfQDfFEY8AnYJwjalXCiosYRBIBZX8" crossorigin="anonymous"></script>
  <script  href="/static/bootstrap.js"></script>
</head>

And this works, you can execute the repo. Why I went this way was I had to customisesome config in bootstrapso I had to change the variables file and build the code of bootstrap which outputted me bootstrap.jsand bootstrap.cssfiles, which I am using here.

这有效,您可以执行回购。我这样做的原因是我必须在引导程序定义一些配置,所以我必须更改变量文件并构建输出我和文件的引导程序代码,我在这里使用。bootstrap.jsbootstrap.css



There is an alternative way suggested hereby using the npm package and a webpack customisation.

有建议的另一种方法在这里使用NPM包和定制的WebPack。

First install bootstrap in your project:

首先在您的项目中安装引导程序:

npm install [email protected]

And make sure you can use sass-loader in your components:

并确保您可以在组件中使用 sass-loader:

npm install sass-loader node-sass --save-dev

now go to your webpack config file and add a sassLoader object with the following:

现在转到您的 webpack 配置文件并添加一个 sassLoader 对象,其中包含以下内容:

sassLoader: {
    includePaths: [
        path.resolve(projectRoot, 'node_modules/bootstrap/scss/'),
    ],
},

projectRootshould just point to where you can navigate to node_packagesfrom, in my case this is: path.resolve(__dirname, '../')

projectRoot应该只是指向您可以导航的位置node_packages,在我的情况下是:path.resolve(__dirname, '../')

Now you can use bootstrap directly in your .vue files and webpack will compile it for you when you add the following:

现在你可以直接在你的 .vue 文件中使用 bootstrap,当你添加以下内容时,webpack 会为你编译它:

<style lang="scss">
  @import "bootstrap";
</style>

回答by Vahid PG

I highly recommend using bootstrap-loader. You add a config file (.bootstraprcin your root folder) where you can exclude the bootstrap elements you don't want and tell where your variables.scssand bootstrap.overrides.scssare. Define you SCSS variables, do your overrides, add your webpack entry and get on with your life.

我强烈推荐使用bootstrap-loader。您添加一个配置文件(.bootstraprc在你的根文件夹),您可以排除你不想引导要素,并告诉您的variables.scss,并bootstrap.overrides.scss有。定义您的 SCSS 变量,执行您的覆盖,添加您的 webpack 条目并继续您的生活。

回答by Bob Jordan

I use webpack to build bootstrap directly from .less and .scss files. This allows customizing bootstrap by overriding the source in .less/.scss and still get all the benefits of webpack.

我使用 webpack 直接从 .less 和 .scss 文件构建引导程序。这允许通过覆盖 .less/.scss 中的源来自定义引导程序,并且仍然可以获得 webpack 的所有好处。

Your code is missing an entry point for any .css/.less/.scss files. You need to include an entry point for the compiled css files. For this entry point, I declare an array with constand then include paths inside the array to the source files I want webpack to compile.

您的代码缺少任何 .css/.less/.scss 文件的入口点。您需要为已编译的 css 文件包含一个入口点。对于这个入口点,我声明了一个数组,const然后在数组中包含指向我希望 webpack 编译的源文件的路径。

Currently, I'm using bootstrap 3 with a base custom template and also a 2nd custom theme. The base template uses bootstrap .less file styling and it has specific source overrides written in .less files.

目前,我正在使用带有基本自定义模板和第二个自定义主题的引导程序 3。基本模板使用 bootstrap .less 文件样式,并具有写入 .less 文件的特定源代码覆盖。

The 2nd custom theme uses .sass file styling and has similar overrides to bootstrap's base written in .scss files. So, I need to try to optimize all this styling for production (currently coming in about 400kb but that's a little heavy because we choose to avoid CDN's due to targeting use in China).

第二个自定义主题使用 .sass 文件样式,并具有与用 .scss 文件编写的引导程序基础类似的覆盖。所以,我需要尝试优化所有这些用于生产的样式(目前大约有 400kb,但这有点重,因为我们选择避免​​在 CN 使用 CDN)。

Below is a reference webpack.config.js which works to build from .less/.scss/.css files, and also does a few other things like build typescript modules and uses Babel for converting es6/typescript to browser compatible javascript. The output ultimately ends up in my /static/distfolder.

下面是一个参考 webpack.config.js,它可以从 .less/.scss/.css 文件构建,还可以做一些其他的事情,比如构建 typescript 模块和使用 Babel 将 es6/typescript 转换为浏览器兼容的 javascript。输出最终会出现在我的/static/dist文件夹中。

const path = require('path');
const webpack = require('webpack');

// plugins
const ForkTsCheckerNotifierWebpackPlugin = require('fork-ts-checker-notifier-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');


// take debug mode from the environment
const debug = (process.env.NODE_ENV !== 'production');
// Development asset host (webpack dev server)
const publicHost = debug ? 'http://localhost:9001' : '';
const rootAssetPath = path.join(__dirname, 'src');


const manifestOptions = {
    publicPath: `${publicHost}/static/dist/`,
  };

const babelLoader = {
  loader: 'babel-loader',
  options: {
    cacheDirectory: true,
    presets: [
        '@babel/preset-env'
    ]
  }
};

const app_css = [
    // specific order generally matters
    path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'css', 'fonts', 'Roboto', 'css', 'fonts.css'),
    path.join(__dirname, 'node_modules', 'font-awesome', 'css', 'font-awesome.css'),
    // This is bootstrap 3.3.7 base styling writtin in .less
    path.join(__dirname, 'src', 'bootstrap-template1', 'assets',  'less', '_main_full', 'bootstrap.less'),
    // bootstrap theme in .scss -> src\bp\folder\theme\src\scss\styles.scss
    path.join(__dirname, 'src', 'bp', 'folder', 'theme', 'src', 'scss', 'styles.scss'),
    // back to .less -> 'src/bootstrap-template1/assets/less/_main_full/core.less',
    path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'core.less'),
    // 'src/bootstrap-template1/assets/less/_main_full/components.less',
    path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'components.less'),
    //'src/bootstrap-template1/assets/less/_main_full/colors.less',
    path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'colors.less'),
    // <!-- syntax highlighting in .css --> src/bp/folder/static/css/pygments.css
    path.join(__dirname, 'src', 'bp', 'folder', 'static', 'css', 'pygments.css'),
    // <!-- lato/ptsans font we want to serve locally --> src/fonts/googlefonts.css'
    path.join(__dirname, 'src', 'fonts', 'googlefonts.css'),
    // a .css style -> 'src/bp/appbase/styles/md_table_generator.css'
    path.join(__dirname, 'src', 'bp', 'appbase', 'styles', 'md_table_generator.css'),
    // another .css style -> hopscotch 'src/libs/hopscotch/dist/css/hopscotch.min.css'
    path.join(__dirname, 'src', 'libs', 'hopscotch', 'dist', 'css', 'hopscotch.min.css'),
    //LAST final custom snippet styles to ensure they take priority 'src/css/style.css',
    path.join(__dirname, 'src', 'css', 'style.css')
];

const vendor_js = [
    //'core-js',
    'whatwg-fetch',
];

const app_js = [
    // a typescript file! :)
    path.join(__dirname, 'src', 'typescript', 'libs', 'md-table', 'src', 'extension.ts'),
    // base bootstrap 3.3.7 javascript
    path.join(__dirname, 'node_modules', 'bootstrap', 'dist', 'js', 'bootstrap.js'),
    path.join(__dirname, 'src', 'main', 'app.js'),
    // src/bootstrap-template1/assets/js/plugins/forms/styling/uniform.min.js'
    path.join(__dirname, 'node_modules', '@imanov', 'jquery.uniform', 'src', 'js', 'jquery.uniform.js'),
    // src/bootstrap-template1/assets/js/plugins/ui/moment/moment.min.js'
];

function recursiveIssuer(m) {
  if (m.issuer) {
    return recursiveIssuer(m.issuer);
  } else if (m.name) {
    return m.name;
  } else {
    return false;
  }
}

module.exports = {
    context: process.cwd(), // to automatically find tsconfig.json
    // context: __dirname,
    entry: {
        app_css,
        vendor_js,
        app_js,
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        publicPath: `${publicHost}/static/dist/`,
        chunkFilename: '[id].[hash:7].js',
        filename: '[name].[hash:7].js'
    },
    resolve: {
        extensions: [".webpack.js", ".web.js",".tsx", ".ts", ".js", ".css"],
        alias: {
            jquery$: path.resolve(__dirname, 'node_modules', 'jquery', 'dist', 'jquery.js'),
        }
    },
    target: "web",
    devtool: 'source-map',
    devServer: {
        // this devserver proxies all requests to my python development server except 
        // webpack compiled files in the `/static/dist` folder 
        clientLogLevel: 'warning',
        contentBase: path.join(__dirname, './src'),
        publicPath: 'dist',
        open: true,
        historyApiFallback: true,
        stats: 'errors-only',
        headers: {'Access-Control-Allow-Origin': '*'},
        watchContentBase: true,
        port: 9001,
        proxy: {
            '!(/dist/**/**.*)': {
                target: 'http://127.0.0.1:8000',
            },
        },
    },
    mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
    optimization: {
        minimizer: [new TerserJSPlugin({}), new OptimizeCssAssetsPlugin({})],
        splitChunks: {
          cacheGroups: {
            appStyles: {
              name: 'app',
              // https://webpack.js.org/plugins/mini-css-extract-plugin/#extracting-css-based-on-entry
              test: (m, c, entry = 'app') =>
                m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
              chunks: 'all',
              enforce: true,
            },
          },
        },
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
            'window.jQuery': 'jquery',
            'window.$': 'jquery'
        }),
        // Strip all locales from moment.js except "zh-cn"
        // ("en" is built into Moment and can't be removed)
        new MomentLocalesPlugin({
            localesToKeep: ['zh-cn'],
        }),
        new ForkTsCheckerWebpackPlugin({
            tslint: true, useTypescriptIncrementalApi: true
        }),
        new ForkTsCheckerNotifierWebpackPlugin({ title: 'TypeScript', excludeWarnings: false }),
        new MiniCssExtractPlugin({
            filename: '[name].[hash:7].css',
            chunkFilename: '[id].[hash:7].css',
            moduleFilename: ({ name }) => `${name.replace('/js/', '/css/')}.[hash:7].css`
        }),
         new OptimizeCssAssetsPlugin({
            assetNameRegExp: /\.optimize\.css$/g,
            cssProcessor: require('cssnano'),
            cssProcessorPluginOptions: {
            preset: ['default', { discardComments: { removeAll: true } }],
            },
            canPrint: true
        }),
        new ManifestPlugin({
            ...manifestOptions
        }),
    ].concat(debug ? [] : [
        // production webpack plugins go here
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: JSON.stringify('production'),
            }
        }),
        new ForkTsCheckerWebpackPlugin({
            async: false,
            useTypescriptIncrementalApi: true,
            memoryLimit: 2048
        }),
    ]),
    module: {
        rules: [
                {
                // jinja/nunjucks templates
                test: /\.jinja2$/,
                loader: 'jinja-loader',
                query: {
                    root:'../templates'
                }
            },
            {
                test: /\.ts(x?)$/,
                exclude: /node_modules/,
                use: [
                    babelLoader,
                    {
                        loader: 'ts-loader',
                        options:
                            {   // disable type checker - we will use it in
                                // fork-ts-checker-webpack-plugin
                                transpileOnly: true
                            }
                    }
                ]
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    babelLoader
                ]
            },
            {
                test: /\.(html|jinja2)$/,
                loader: 'raw-loader'
            },
            {
                test: /\.(sc|sa|c)ss$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                          hmr: debug,
                            // only use if hmr is not working correctly
                          // reloadAll: true,
                        },
                    },
                    {
                        loader: "css-loader",
                    },
                    {
                        loader: "sass-loader"
                    },
                ]
            },
            {
                test: /\.less$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                          hmr: debug,
                            // only use if hmr is not working correctly
                          // reloadAll: true,
                        },
                    },
                    {
                        loader: 'css-loader', // translates CSS into CommonJS
                    },
                    {
                        loader: 'less-loader', // compiles Less to CSS
                    },
                ],
            },
            {
                test: /\.(ttf|eot|svg|gif|ico)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: '[path][name].[hash:7].[ext]',
                            context: rootAssetPath
                        },
                    },
                ],
            },
            {
                test: /\.(jpe?g|png)$/i,
                loader: 'responsive-loader',
                options: {
                    name: '[path][name].[hash:7].[ext]',
                    adapter: require('responsive-loader/sharp'),
                    context: rootAssetPath
                }
            },
            {
                test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                 loader:'url-loader',
                 options:{
                    limit: 10000,
                    mimetype: 'application/font-woff',
                    // name: ('fonts/[path][name].[hash:7].[ext]'),
                    name: ('fonts/[name].[hash:7].[ext]'),
                 }
            },
            {
                test: require.resolve("jquery"),
                use:[
                    { loader: "expose-loader", options:"$" },
                    { loader: "expose-loader", options:"jQuery" },
                    { loader: "expose-loader", options:"jquery" }
                ]
            }
        ]
    },
};